diff options
Diffstat (limited to 'services/java/com/android/server')
9 files changed, 1018 insertions, 378 deletions
diff --git a/services/java/com/android/server/GadgetService.java b/services/java/com/android/server/GadgetService.java index 5ef0fb9..ddf3afe 100644 --- a/services/java/com/android/server/GadgetService.java +++ b/services/java/com/android/server/GadgetService.java @@ -31,9 +31,10 @@ import android.content.pm.PackageItemInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.gadget.GadgetManager; -import android.gadget.GadgetInfo; +import android.gadget.GadgetProviderInfo; import android.net.Uri; import android.os.Binder; +import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; import android.util.AttributeSet; @@ -78,7 +79,7 @@ class GadgetService extends IGadgetService.Stub static class Provider { int uid; - GadgetInfo info; + GadgetProviderInfo info; ArrayList<GadgetId> instances = new ArrayList(); PendingIntent broadcast; @@ -106,7 +107,7 @@ class GadgetService extends IGadgetService.Stub PackageManager mPackageManager; AlarmManager mAlarmManager; ArrayList<Provider> mInstalledProviders = new ArrayList(); - int mNextGadgetId = 1; + int mNextGadgetId = GadgetManager.INVALID_GADGET_ID + 1; ArrayList<GadgetId> mGadgetIds = new ArrayList(); ArrayList<Host> mHosts = new ArrayList(); @@ -128,7 +129,6 @@ class GadgetService extends IGadgetService.Stub // update the provider list. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); - filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -148,7 +148,7 @@ class GadgetService extends IGadgetService.Stub int N = mInstalledProviders.size(); pw.println("Providers: (size=" + N + ")"); for (int i=0; i<N; i++) { - GadgetInfo info = mInstalledProviders.get(i).info; + GadgetProviderInfo info = mInstalledProviders.get(i).info; pw.println(" [" + i + "] provder=" + info.provider + " min=(" + info.minWidth + "x" + info.minHeight + ")" + " updatePeriodMillis=" + info.updatePeriodMillis @@ -265,7 +265,7 @@ class GadgetService extends IGadgetService.Stub if (p != null) { p.instances.remove(id); // send the broacast saying that this gadgetId has been deleted - Intent intent = new Intent(GadgetManager.GADGET_DELETED_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_DELETED); intent.setComponent(p.info.provider); intent.putExtra(GadgetManager.EXTRA_GADGET_ID, id.gadgetId); mContext.sendBroadcast(intent); @@ -274,7 +274,7 @@ class GadgetService extends IGadgetService.Stub cancelBroadcasts(p); // send the broacast saying that the provider is not in use any more - intent = new Intent(GadgetManager.GADGET_DISABLED_ACTION); + intent = new Intent(GadgetManager.ACTION_GADGET_DISABLED); intent.setComponent(p.info.provider); mContext.sendBroadcast(intent); } @@ -331,7 +331,7 @@ class GadgetService extends IGadgetService.Stub } } - public GadgetInfo getGadgetInfo(int gadgetId) { + public GadgetProviderInfo getGadgetInfo(int gadgetId) { synchronized (mGadgetIds) { GadgetId id = lookupGadgetIdLocked(gadgetId); if (id != null) { @@ -351,10 +351,10 @@ class GadgetService extends IGadgetService.Stub } } - public List<GadgetInfo> getInstalledProviders() { + public List<GadgetProviderInfo> getInstalledProviders() { synchronized (mGadgetIds) { final int N = mInstalledProviders.size(); - ArrayList<GadgetInfo> result = new ArrayList(N); + ArrayList<GadgetProviderInfo> result = new ArrayList(N); for (int i=0; i<N; i++) { result.add(mInstalledProviders.get(i).info); } @@ -364,7 +364,7 @@ class GadgetService extends IGadgetService.Stub public void updateGadgetIds(int[] gadgetIds, RemoteViews views) { if (gadgetIds == null) { - throw new IllegalArgumentException("bad gadgetIds"); + return; } if (gadgetIds.length == 0) { return; @@ -408,7 +408,7 @@ class GadgetService extends IGadgetService.Stub // the lock is held, but this is a oneway call id.host.callbacks.updateGadget(id.gadgetId, views); } catch (RemoteException e) { - // It failed, remove the callback. No need to prune because + // It failed; remove the callback. No need to prune because // we know that this host is still referenced by this instance. id.host.callbacks = null; } @@ -524,10 +524,7 @@ class GadgetService extends IGadgetService.Stub void getGadgetList() { PackageManager pm = mPackageManager; - // TODO: We have these as different actions. I wonder if it makes more sense to - // have like a GADGET_ACTION, and then subcommands. It's kind of arbitrary that - // we look for GADGET_UPDATE_ACTION and not any of the other gadget actions. - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -538,11 +535,14 @@ class GadgetService extends IGadgetService.Stub } } - void addProviderLocked(ResolveInfo ri) { - Provider p = parseGadgetInfoXml(new ComponentName(ri.activityInfo.packageName, + boolean addProviderLocked(ResolveInfo ri) { + Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name), ri); if (p != null) { mInstalledProviders.add(p); + return true; + } else { + return false; } } @@ -568,16 +568,18 @@ class GadgetService extends IGadgetService.Stub } void sendEnableIntentLocked(Provider p) { - Intent intent = new Intent(GadgetManager.GADGET_ENABLED_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_ENABLED); intent.setComponent(p.info.provider); mContext.sendBroadcast(intent); } void sendUpdateIntentLocked(Provider p, int[] gadgetIds) { - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); - intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); - intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent); + if (gadgetIds != null && gadgetIds.length > 0) { + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); + intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); + intent.setComponent(p.info.provider); + mContext.sendBroadcast(intent); + } } void registerForBroadcastsLocked(Provider p, int[] gadgetIds) { @@ -587,7 +589,7 @@ class GadgetService extends IGadgetService.Stub // PendingIntent.getBroadcast will update the extras. boolean alreadyRegistered = p.broadcast != null; int instancesSize = p.instances.size(); - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); intent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds); intent.setComponent(p.info.provider); long token = Binder.clearCallingIdentity(); @@ -614,16 +616,16 @@ class GadgetService extends IGadgetService.Stub return gadgetIds; } - private Provider parseGadgetInfoXml(ComponentName component, ResolveInfo ri) { + private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) { Provider p = null; ActivityInfo activityInfo = ri.activityInfo; XmlResourceParser parser = null; try { parser = activityInfo.loadXmlMetaData(mPackageManager, - GadgetManager.GADGET_PROVIDER_META_DATA); + GadgetManager.META_DATA_GADGET_PROVIDER); if (parser == null) { - Log.w(TAG, "No " + GadgetManager.GADGET_PROVIDER_META_DATA + " meta-data for " + Log.w(TAG, "No " + GadgetManager.META_DATA_GADGET_PROVIDER + " meta-data for " + "gadget provider '" + component + '\''); return null; } @@ -644,7 +646,7 @@ class GadgetService extends IGadgetService.Stub } p = new Provider(); - GadgetInfo info = p.info = new GadgetInfo(); + GadgetProviderInfo info = p.info = new GadgetProviderInfo(); info.provider = component; p.uid = activityInfo.applicationInfo.uid; @@ -672,6 +674,7 @@ class GadgetService extends IGadgetService.Stub // of what a client process passes to us should not be fatal for the // system process. Log.w(TAG, "XML parsing failed for gadget provider '" + component + '\'', e); + return null; } finally { if (parser != null) parser.close(); } @@ -979,20 +982,26 @@ class GadgetService extends IGadgetService.Stub if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { synchronized (mGadgetIds) { - addProvidersForPackageLocked(pkgName); - saveStateLocked(); - } - } - else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) { - synchronized (mGadgetIds) { - updateProvidersForPackageLocked(pkgName); + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package was just upgraded + updateProvidersForPackageLocked(pkgName); + } else { + // The package was just added + addProvidersForPackageLocked(pkgName); + } saveStateLocked(); } } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - synchronized (mGadgetIds) { - removeProvidersForPackageLocked(pkgName); - saveStateLocked(); + Bundle extras = intent.getExtras(); + if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) { + // The package is being updated. We'll receive a PACKAGE_ADDED shortly. + } else { + synchronized (mGadgetIds) { + removeProvidersForPackageLocked(pkgName); + saveStateLocked(); + } } } } @@ -1002,7 +1011,7 @@ class GadgetService extends IGadgetService.Stub // TODO: If there's a better way of matching an intent filter against the // packages for a given package, use that. void addProvidersForPackageLocked(String pkgName) { - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1021,7 +1030,7 @@ class GadgetService extends IGadgetService.Stub // packages for a given package, use that. void updateProvidersForPackageLocked(String pkgName) { HashSet<String> keep = new HashSet(); - Intent intent = new Intent(GadgetManager.GADGET_UPDATE_ACTION); + Intent intent = new Intent(GadgetManager.ACTION_GADGET_UPDATE); List<ResolveInfo> broadcastReceivers = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); @@ -1031,17 +1040,53 @@ class GadgetService extends IGadgetService.Stub ResolveInfo ri = broadcastReceivers.get(i); ActivityInfo ai = ri.activityInfo; if (pkgName.equals(ai.packageName)) { - Provider p = lookupProviderLocked(new ComponentName(ai.packageName, ai.name)); + ComponentName component = new ComponentName(ai.packageName, ai.name); + Provider p = lookupProviderLocked(component); if (p == null) { - addProviderLocked(ri); + if (addProviderLocked(ri)) { + keep.add(ai.name); + } + } else { + Provider parsed = parseProviderInfoXml(component, ri); + if (parsed != null) { + keep.add(ai.name); + // Use the new GadgetProviderInfo. + GadgetProviderInfo oldInfo = p.info; + p.info = parsed.info; + // If it's enabled + final int M = p.instances.size(); + if (M > 0) { + int[] gadgetIds = getGadgetIds(p); + // Reschedule for the new updatePeriodMillis (don't worry about handling + // it specially if updatePeriodMillis didn't change because we just sent + // an update, and the next one will be updatePeriodMillis from now). + cancelBroadcasts(p); + registerForBroadcastsLocked(p, gadgetIds); + // If it's currently showing, call back with the new GadgetProviderInfo. + for (int j=0; j<M; j++) { + GadgetId id = p.instances.get(j); + if (id.host != null && id.host.callbacks != null) { + try { + id.host.callbacks.providerChanged(id.gadgetId, p.info); + } catch (RemoteException ex) { + // It failed; remove the callback. No need to prune because + // we know that this host is still referenced by this + // instance. + id.host.callbacks = null; + } + } + } + // Now that we've told the host, push out an update. + sendUpdateIntentLocked(p, gadgetIds); + } + } } - keep.add(ai.name); } } // prune the ones we don't want to keep N = mInstalledProviders.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName()) && !keep.contains(p.info.provider.getClassName())) { @@ -1052,7 +1097,7 @@ class GadgetService extends IGadgetService.Stub void removeProvidersForPackageLocked(String pkgName) { int N = mInstalledProviders.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Provider p = mInstalledProviders.get(i); if (pkgName.equals(p.info.provider.getPackageName())) { removeProviderLocked(i, p); @@ -1064,7 +1109,7 @@ class GadgetService extends IGadgetService.Stub // By now, we have removed any gadgets that were in any hosts here, // so we don't need to worry about sending DISABLE broadcasts to them. N = mHosts.size(); - for (int i=0; i<N; i++) { + for (int i=N-1; i>=0; i--) { Host host = mHosts.get(i); if (pkgName.equals(host.packageName)) { deleteHostLocked(host); diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index ee49365..7588129 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -55,8 +55,10 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.provider.Settings; import android.text.TextUtils; +import android.util.EventLog; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; @@ -98,6 +100,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_UNBIND_METHOD = 3000; static final int MSG_BIND_METHOD = 3010; + static final long TIME_TO_RECONNECT = 10*1000; + + static final int LOG_IMF_FORCE_RECONNECT_IME = 32000; + final Context mContext; final Handler mHandler; final SettingsObserver mSettingsObserver; @@ -248,6 +254,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub IInputMethod mCurMethod; /** + * Time that we last initiated a bind to the input method, to determine + * if we should try to disconnect and reconnect to it. + */ + long mLastBindTime; + + /** * Have we called mCurMethod.bindInput()? */ boolean mBoundToMethod; @@ -486,7 +498,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void systemReady() { - } public List<InputMethodInfo> getInputMethodList() { @@ -571,7 +582,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - private int getShowFlags() { + private int getImeShowFlags() { int flags = 0; if (mShowForced) { flags |= InputMethod.SHOW_FORCED @@ -582,6 +593,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return flags; } + private int getAppShowFlags() { + int flags = 0; + if (mShowForced) { + flags |= InputMethodManager.SHOW_FORCED; + } else if (!mShowExplicitlyRequested) { + flags |= InputMethodManager.SHOW_IMPLICIT; + } + return flags; + } + InputBindResult attachNewInputLocked(boolean initial, boolean needResult) { if (!mBoundToMethod) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( @@ -598,7 +619,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mShowRequested) { if (DEBUG) Log.v(TAG, "Attach new input asks to show input"); - showCurrentInputLocked(getShowFlags()); + showCurrentInputLocked(getAppShowFlags()); } return needResult ? new InputBindResult(session.session, mCurId, mCurSeq) @@ -666,14 +687,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return attachNewInputLocked(initial, needResult); } if (mHaveConnection) { - if (mCurMethod != null && !cs.sessionRequested) { - cs.sessionRequested = true; - if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); - executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_CREATE_SESSION, mCurMethod, - new MethodCallback(mCurMethod))); + if (mCurMethod != null) { + if (!cs.sessionRequested) { + cs.sessionRequested = true; + if (DEBUG) Log.v(TAG, "Creating new session for client " + cs); + executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( + MSG_CREATE_SESSION, mCurMethod, + new MethodCallback(mCurMethod))); + } + // Return to client, and we will get back with it when + // we have had a session made for it. + return new InputBindResult(null, mCurId, mCurSeq); + } else if (SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // In this case we have connected to the service, but + // don't yet have its interface. If it hasn't been too + // long since we did the connection, we'll return to + // the client and wait to get the service interface so + // we can report back. If it has been too long, we want + // to fall through so we can try a disconnect/reconnect + // to see if we can get back in touch with the service. + return new InputBindResult(null, mCurId, mCurSeq); + } else { + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime, 0); } - return new InputBindResult(null, mCurId, mCurSeq); } } @@ -682,6 +720,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub throw new IllegalArgumentException("Unknown id: " + mCurMethodId); } + if (mHaveConnection) { + mContext.unbindService(this); + mHaveConnection = false; + } + if (mCurToken != null) { try { if (DEBUG) Log.v(TAG, "Removing window token: " + mCurToken); @@ -691,16 +734,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurToken = null; } - if (mHaveConnection) { - mContext.unbindService(this); - mHaveConnection = false; - } - clearCurMethod(); mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE); mCurIntent.setComponent(info.getComponent()); if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) { + mLastBindTime = SystemClock.uptimeMillis(); mHaveConnection = true; mCurId = info.getId(); mCurToken = new Binder(); @@ -758,7 +797,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub void onSessionCreated(IInputMethod method, IInputMethodSession session) { synchronized (mMethodMap) { - if (mCurMethod == method) { + if (mCurMethod != null && method != null + && mCurMethod.asBinder() == method.asBinder()) { if (mCurClient != null) { mCurClient.curSession = new SessionState(mCurClient, method, session); @@ -781,6 +821,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mCurMethod = null; } + mStatusBar.setIconVisibility(mInputMethodIcon, false); } public void onServiceDisconnected(ComponentName name) { @@ -790,6 +831,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurMethod != null && mCurIntent != null && name.equals(mCurIntent.getComponent())) { clearCurMethod(); + // We consider this to be a new bind attempt, since the system + // should now try to restart the service for us. + mLastBindTime = SystemClock.uptimeMillis(); mShowRequested = mInputShown; mInputShown = false; if (mCurClient != null) { @@ -800,23 +844,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public void updateStatusIcon(int iconId, String iconPackage) { - if (iconId == 0) { - Log.d(TAG, "hide the small icon for the input method"); - mStatusBar.setIconVisibility(mInputMethodIcon, false); - } else { - Log.d(TAG, "show a small icon for the input method"); - - if (iconPackage != null - && iconPackage - .equals(InputMethodManager.BUILDIN_INPUTMETHOD_PACKAGE)) { - iconPackage = null; + public void updateStatusIcon(IBinder token, String packageName, int iconId) { + long ident = Binder.clearCallingIdentity(); + try { + if (token == null || mCurToken != token) { + Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - - mInputMethodData.iconId = iconId; - mInputMethodData.iconPackage = iconPackage; - mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); - mStatusBar.setIconVisibility(mInputMethodIcon, true); + + synchronized (mMethodMap) { + if (iconId == 0) { + if (DEBUG) Log.d(TAG, "hide the small icon for the input method"); + mStatusBar.setIconVisibility(mInputMethodIcon, false); + } else if (packageName != null) { + if (DEBUG) Log.d(TAG, "show a small icon for the input method"); + mInputMethodData.iconId = iconId; + mInputMethodData.iconPackage = packageName; + mStatusBar.updateIcon(mInputMethodIcon, mInputMethodData, null); + mStatusBar.setIconVisibility(mInputMethodIcon, true); + } + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -860,23 +909,28 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } public void showSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring showSoftInput of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring showSoftInput of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be shown"); + showCurrentInputLocked(flags); } - - if (DEBUG) Log.v(TAG, "Client requesting input be shown"); - showCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -891,29 +945,44 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurMethod != null) { executeOrSendMessage(mCurMethod, mCaller.obtainMessageIO( - MSG_SHOW_SOFT_INPUT, getShowFlags(), mCurMethod)); + MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod)); mInputShown = true; + } else if (mHaveConnection && SystemClock.uptimeMillis() + < (mLastBindTime+TIME_TO_RECONNECT)) { + // The client has asked to have the input method shown, but + // we have been sitting here too long with a connection to the + // service and no interface received, so let's disconnect/connect + // to try to prod things along. + EventLog.writeEvent(LOG_IMF_FORCE_RECONNECT_IME, mCurMethodId, + SystemClock.uptimeMillis()-mLastBindTime,1); + mContext.unbindService(this); + mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE); } } public void hideSoftInput(IInputMethodClient client, int flags) { - synchronized (mMethodMap) { - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring hideSoftInput of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring hideSoftInput of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } + + if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); + hideCurrentInputLocked(flags); } - - if (DEBUG) Log.v(TAG, "Client requesting input be hidden"); - hideCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -942,70 +1011,75 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void windowGainedFocus(IInputMethodClient client, boolean viewHasFocus, boolean isTextEditor, int softInputMode, boolean first, int windowFlags) { - synchronized (mMethodMap) { - if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() - + " viewHasFocus=" + viewHasFocus - + " isTextEditor=" + isTextEditor - + " softInputMode=#" + Integer.toHexString(softInputMode) - + " first=" + first + " flags=#" - + Integer.toHexString(windowFlags)); - - if (mCurClient == null || client == null - || mCurClient.client.asBinder() != client.asBinder()) { - try { - // We need to check if this is the current client with - // focus in the window manager, to allow this call to - // be made before input is started in it. - if (!mIWindowManager.inputMethodClientHasFocus(client)) { - Log.w(TAG, "Ignoring focus gain of: " + client); - return; + long ident = Binder.clearCallingIdentity(); + try { + synchronized (mMethodMap) { + if (DEBUG) Log.v(TAG, "windowGainedFocus: " + client.asBinder() + + " viewHasFocus=" + viewHasFocus + + " isTextEditor=" + isTextEditor + + " softInputMode=#" + Integer.toHexString(softInputMode) + + " first=" + first + " flags=#" + + Integer.toHexString(windowFlags)); + + if (mCurClient == null || client == null + || mCurClient.client.asBinder() != client.asBinder()) { + try { + // We need to check if this is the current client with + // focus in the window manager, to allow this call to + // be made before input is started in it. + if (!mIWindowManager.inputMethodClientHasFocus(client)) { + Log.w(TAG, "Ignoring focus gain of: " + client); + return; + } + } catch (RemoteException e) { } - } catch (RemoteException e) { } - } - - switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: - if (!isTextEditor || (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { - if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { - // There is no focus view, and this window will - // be behind any soft input window, so hide the - // soft input window if it is shown. - if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); - hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS); + + switch (softInputMode&WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE) { + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED: + if (!isTextEditor || (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + != WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) { + if (WindowManager.LayoutParams.mayUseInputMethod(windowFlags)) { + // There is no focus view, and this window will + // be behind any soft input window, so hide the + // soft input window if it is shown. + if (DEBUG) Log.v(TAG, "Unspecified window will hide input"); + hideCurrentInputLocked(InputMethodManager.HIDE_NOT_ALWAYS); + } + } else if (isTextEditor && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) + == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE + && (softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + // There is a focus view, and we are navigating forward + // into the window, so show the input window for the user. + if (DEBUG) Log.v(TAG, "Unspecified window will show input"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); } - } else if (isTextEditor && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) - == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - && (softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - // There is a focus view, and we are navigating forward - // into the window, so show the input window for the user. - if (DEBUG) Log.v(TAG, "Unspecified window will show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: - // Do nothing. - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: - if (DEBUG) Log.v(TAG, "Window asks to hide input"); - hideCurrentInputLocked(0); - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: - if ((softInputMode & - WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { - if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED: + // Do nothing. + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN: + if (DEBUG) Log.v(TAG, "Window asks to hide input"); + hideCurrentInputLocked(0); + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE: + if ((softInputMode & + WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) { + if (DEBUG) Log.v(TAG, "Window asks to show input going forward"); + showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); + } + break; + case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: + if (DEBUG) Log.v(TAG, "Window asks to always show input"); showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - } - break; - case WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE: - if (DEBUG) Log.v(TAG, "Window asks to always show input"); - showCurrentInputLocked(InputMethodManager.SHOW_IMPLICIT); - break; + break; + } } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -1022,7 +1096,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void setInputMethod(IBinder token, String id) { synchronized (mMethodMap) { - if (mCurToken == null) { + if (token == null) { if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { @@ -1032,19 +1106,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } else if (mCurToken != token) { Log.w(TAG, "Ignoring setInputMethod of token: " + token); + return; } - setInputMethodLocked(id); + long ident = Binder.clearCallingIdentity(); + try { + setInputMethodLocked(id); + } finally { + Binder.restoreCallingIdentity(ident); + } } } public void hideMySoftInput(IBinder token, int flags) { synchronized (mMethodMap) { - if (mCurToken == null || mCurToken != token) { + if (token == null || mCurToken != token) { Log.w(TAG, "Ignoring hideInputMethod of token: " + token); + return; } - hideCurrentInputLocked(flags); + long ident = Binder.clearCallingIdentity(); + try { + hideCurrentInputLocked(flags); + } finally { + Binder.restoreCallingIdentity(ident); + } } } @@ -1209,7 +1295,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // ---------------------------------------------------------------------- - public void showInputMethodMenu() { + void showInputMethodMenu() { if (DEBUG) Log.v(TAG, "Show switching menu"); hideInputMethodMenu(); @@ -1309,83 +1395,88 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + android.Manifest.permission.WRITE_SECURE_SETTINGS); } - // Make sure this is a valid input method. - InputMethodInfo imm = mMethodMap.get(id); - if (imm == null) { + long ident = Binder.clearCallingIdentity(); + try { + // Make sure this is a valid input method. + InputMethodInfo imm = mMethodMap.get(id); if (imm == null) { - throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + if (imm == null) { + throw new IllegalArgumentException("Unknown id: " + mCurMethodId); + } } - } - - StringBuilder builder = new StringBuilder(256); - - boolean removed = false; - String firstId = null; - - // Look through the currently enabled input methods. - String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS); - if (enabledStr != null) { - final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; - splitter.setString(enabledStr); - while (splitter.hasNext()) { - String curId = splitter.next(); - if (curId.equals(id)) { - if (enabled) { - // We are enabling this input method, but it is - // already enabled. Nothing to do. The previous - // state was enabled. - return true; + + StringBuilder builder = new StringBuilder(256); + + boolean removed = false; + String firstId = null; + + // Look through the currently enabled input methods. + String enabledStr = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS); + if (enabledStr != null) { + final TextUtils.SimpleStringSplitter splitter = mStringColonSplitter; + splitter.setString(enabledStr); + while (splitter.hasNext()) { + String curId = splitter.next(); + if (curId.equals(id)) { + if (enabled) { + // We are enabling this input method, but it is + // already enabled. Nothing to do. The previous + // state was enabled. + return true; + } + // We are disabling this input method, and it is + // currently enabled. Skip it to remove from the + // new list. + removed = true; + } else if (!enabled) { + // We are building a new list of input methods that + // doesn't contain the given one. + if (firstId == null) firstId = curId; + if (builder.length() > 0) builder.append(':'); + builder.append(curId); } - // We are disabling this input method, and it is - // currently enabled. Skip it to remove from the - // new list. - removed = true; - } else if (!enabled) { - // We are building a new list of input methods that - // doesn't contain the given one. - if (firstId == null) firstId = curId; - if (builder.length() > 0) builder.append(':'); - builder.append(curId); } } - } - - if (!enabled) { - if (!removed) { - // We are disabling the input method but it is already - // disabled. Nothing to do. The previous state was - // disabled. - return false; - } - // Update the setting with the new list of input methods. - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); - // We the disabled input method is currently selected, switch - // to another one. - String selId = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD); - if (id.equals(selId)) { + + if (!enabled) { + if (!removed) { + // We are disabling the input method but it is already + // disabled. Nothing to do. The previous state was + // disabled. + return false; + } + // Update the setting with the new list of input methods. Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.DEFAULT_INPUT_METHOD, - firstId != null ? firstId : ""); + Settings.Secure.ENABLED_INPUT_METHODS, builder.toString()); + // We the disabled input method is currently selected, switch + // to another one. + String selId = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + if (id.equals(selId)) { + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD, + firstId != null ? firstId : ""); + } + // Previous state was enabled. + return true; } - // Previous state was enabled. - return true; - } - - // Add in the newly enabled input method. - if (enabledStr == null || enabledStr.length() == 0) { - enabledStr = id; - } else { - enabledStr = enabledStr + ':' + id; + + // Add in the newly enabled input method. + if (enabledStr == null || enabledStr.length() == 0) { + enabledStr = id; + } else { + enabledStr = enabledStr + ':' + id; + } + + Settings.Secure.putString(mContext.getContentResolver(), + Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); + + // Previous state was disabled. + return false; + } finally { + Binder.restoreCallingIdentity(ident); } - - Settings.Secure.putString(mContext.getContentResolver(), - Settings.Secure.ENABLED_INPUT_METHODS, enabledStr); - - // Previous state was disabled. - return false; } } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index bc6fd71..db4daa5 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -132,7 +132,7 @@ public class LocationManagerService extends ILocationManager.Stub private static final int MESSAGE_HEARTBEAT = 1; private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2; private static final int MESSAGE_RELEASE_WAKE_LOCK = 3; - private static final int MESSAGE_SET_NETWORK_LOCATION_PROVIDER = 4; + private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4; // Alarm manager and wakelock variables private final static String ALARM_INTENT = "com.android.location.ALARM_INTENT"; @@ -565,13 +565,21 @@ public class LocationManagerService extends ILocationManager.Stub } } - public void setNetworkLocationProvider(INetworkLocationProvider provider) { - mLocationHandler.removeMessages(MESSAGE_SET_NETWORK_LOCATION_PROVIDER); + public void setInstallCallback(InstallCallback callback) { + mLocationHandler.removeMessages(MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER); Message m = Message.obtain(mLocationHandler, - MESSAGE_SET_NETWORK_LOCATION_PROVIDER, provider); + MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER, callback); mLocationHandler.sendMessageAtFrontOfQueue(m); } + public void setNetworkLocationProvider(INetworkLocationProvider provider) { + mNetworkLocationInterface = provider; + provider.addListener(getPackageNames()); + mNetworkLocationProvider = (LocationProviderImpl)provider; + LocationProviderImpl.addProvider(mNetworkLocationProvider); + updateProviders(); + } + public void setLocationCollector(ILocationCollector collector) { mCollector = collector; if (mGpsLocationProvider != null) { @@ -1598,16 +1606,12 @@ public class LocationManagerService extends ILocationManager.Stub // Update wakelock status so the next alarm is set before releasing wakelock updateWakelockStatus(mScreenOn); releaseWakeLock(); - } else if (msg.what == MESSAGE_SET_NETWORK_LOCATION_PROVIDER) { + } else if (msg.what == MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER) { synchronized (LocationManagerService.class) { - Log.d(TAG, "adding network location provider"); - mNetworkLocationInterface = - (INetworkLocationProvider)msg.obj; - mNetworkLocationInterface.addListener(getPackageNames()); - mNetworkLocationProvider = - (LocationProviderImpl)mNetworkLocationInterface; - LocationProviderImpl.addProvider(mNetworkLocationProvider); - updateProviders(); + Log.d(TAG, "installing network location provider"); + INetworkLocationManager.InstallCallback callback = + (INetworkLocationManager.InstallCallback)msg.obj; + callback.installNetworkLocationProvider(mContext, LocationManagerService.this); } } } catch (Exception e) { diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 221ba46..c490e42 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -3219,12 +3219,15 @@ class PackageManagerService extends IPackageManager.Stub { Log.i(TAG, "Observer no longer exists."); } } - // There appears to be a subtle deadlock condition if the sendPackageBroadcast call appears - // in the synchronized block above. + // There appears to be a subtle deadlock condition if the sendPackageBroadcast + // call appears in the synchronized block above. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { res.removedInfo.sendBroadcast(false, true); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); + if (res.removedInfo.removedPackage != null) { + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, res.pkg.applicationInfo.packageName, extras); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 7c111d3..6974b5e 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -1001,11 +1001,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage } } else { - synchronized (mLocks) { - EventLog.writeEvent(LOG_POWER_SCREEN_BROADCAST_STOP, 4, - mBroadcastWakeLock.mCount); - mBroadcastWakeLock.release(); - } + // If we're in this case, then this handler is running for a previous + // paired transaction. mBroadcastWakeLock will already have been released + // in sendNotificationLocked. } } }; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index c009fac..eece581 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -87,10 +87,13 @@ public class WifiService extends IWifiManager.Stub { private final LockList mLocks = new LockList(); /** - * See {@link Settings.System#WIFI_IDLE_MS}. This is the default value if a - * Settings.System value is not present. + * See {@link Settings.Gservices#WIFI_IDLE_MS}. This is the default value if a + * Settings.Gservices value is not present. This timeout value is chosen as + * the approximate point at which the battery drain caused by Wi-Fi + * being enabled but not active exceeds the battery drain caused by + * re-establishing a connection to the mobile data network. */ - private static final long DEFAULT_IDLE_MILLIS = 2 * 60 * 1000; /* 2 minutes */ + private static final long DEFAULT_IDLE_MILLIS = 15 * 60 * 1000; /* 15 minutes */ private static final String WAKELOCK_TAG = "WifiService"; @@ -101,7 +104,7 @@ public class WifiService extends IWifiManager.Stub { * provides a bit of extra margin. * <p> * See {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}. - * This is the default value if a Settings.System value is not present. + * This is the default value if a Settings.Secure value is not present. */ private static final int DEFAULT_WAKELOCK_TIMEOUT = 8000; @@ -1364,7 +1367,7 @@ public class WifiService extends IWifiManager.Stub { * in the current regulatory domain. This method should be used only * if the correct number of channels cannot be determined automatically * for some reason. If the operation is successful, the new value is - * persisted as a System setting. + * persisted as a Secure setting. * @param numChannels the number of allowed channels. Must be greater than 0 * and less than or equal to 16. * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., @@ -1446,8 +1449,8 @@ public class WifiService extends IWifiManager.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - long idleMillis = Settings.System.getLong(mContext.getContentResolver(), - Settings.System.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); + long idleMillis = Settings.Gservices.getLong(mContext.getContentResolver(), + Settings.Gservices.WIFI_IDLE_MS, DEFAULT_IDLE_MILLIS); int stayAwakeConditions = Settings.System.getInt(mContext.getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, 0); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 09f5d8f..fed6d12 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -768,27 +768,55 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (willMove && w != null) { final WindowState curTarget = mInputMethodTarget; if (curTarget != null && curTarget.mAppToken != null) { - int curIndex = -1; - if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" - + mNextAppTransition + " curTarget animating=" - + curTarget.isAnimating() - + " layer=" + curTarget.mAnimLayer - + " new layer=" + w.mAnimLayer); - if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { - // If we are currently setting up for an animation, - // hold everything until we can find out what will happen. - mInputMethodTargetWaitingAnim = true; - curIndex = localmWindows.indexOf(curTarget); - } else if (curTarget.isAnimating() && - curTarget.mAnimLayer > w.mAnimLayer) { - // If the window we are currently targeting is involved - // with an animation, and it is on top of the next target - // we will be over, then hold off on moving until - // that is done. - curIndex = localmWindows.indexOf(curTarget); - } - if (curIndex >= 0) { - return curIndex + 1; + + // Now some fun for dealing with window animations that + // modify the Z order. We need to look at all windows below + // the current target that are in this app, finding the highest + // visible one in layering. + AppWindowToken token = curTarget.mAppToken; + WindowState highestTarget = null; + int highestPos = 0; + if (token.animating || token.animation != null) { + int pos = 0; + pos = localmWindows.indexOf(curTarget); + while (pos >= 0) { + WindowState win = (WindowState)localmWindows.get(pos); + if (win.mAppToken != token) { + break; + } + if (!win.mRemoved) { + if (highestTarget == null || win.mAnimLayer > + highestTarget.mAnimLayer) { + highestTarget = win; + highestPos = pos; + } + } + pos--; + } + } + + if (highestTarget != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "mNextAppTransition=" + + mNextAppTransition + " " + highestTarget + + " animating=" + highestTarget.isAnimating() + + " layer=" + highestTarget.mAnimLayer + + " new layer=" + w.mAnimLayer); + + if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) { + // If we are currently setting up for an animation, + // hold everything until we can find out what will happen. + mInputMethodTargetWaitingAnim = true; + mInputMethodTarget = highestTarget; + return highestPos + 1; + } else if (highestTarget.isAnimating() && + highestTarget.mAnimLayer > w.mAnimLayer) { + // If the window we are currently targeting is involved + // with an animation, and it is on top of the next target + // we will be over, then hold off on moving until + // that is done. + mInputMethodTarget = highestTarget; + return highestPos + 1; + } } } } @@ -891,12 +919,27 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + void logWindowList(String prefix) { + int N = mWindows.size(); + while (N > 0) { + N--; + Log.v(TAG, prefix + "#" + N + ": " + mWindows.get(N)); + } + } + void moveInputMethodDialogsLocked(int pos) { ArrayList<WindowState> dialogs = mInputMethodDialogs; + final int N = dialogs.size(); + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Removing " + N + " dialogs w/pos=" + pos); for (int i=0; i<N; i++) { pos = tmpRemoveWindowLocked(pos, dialogs.get(i)); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Window list w/pos=" + pos); + logWindowList(" "); + } + if (pos >= 0) { final AppWindowToken targetAppToken = mInputMethodTarget.mAppToken; if (pos < mWindows.size()) { @@ -905,17 +948,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pos++; } } + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Adding " + N + " dialogs at pos=" + pos); for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = targetAppToken; pos = reAddWindowLocked(pos, win); } + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Final window list:"); + logWindowList(" "); + } return; } for (int i=0; i<N; i++) { WindowState win = dialogs.get(i); win.mTargetAppToken = null; reAddWindowToListInOrderLocked(win); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "No IM target, final list:"); + logWindowList(" "); + } } } @@ -971,9 +1023,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } if (imWin != null) { + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "Moving IM from " + imPos); + logWindowList(" "); + } imPos = tmpRemoveWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving with new pos " + imPos + ":"); + logWindowList(" "); + } imWin.mTargetAppToken = mInputMethodTarget.mAppToken; reAddWindowLocked(imPos, imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List after moving IM to " + imPos + ":"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(imPos+1); } else { moveInputMethodDialogsLocked(imPos); @@ -984,9 +1048,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // because they aren't currently associated with a focus window. if (imWin != null) { + if (DEBUG_INPUT_METHOD) Log.v(TAG, "Moving IM from " + imPos); tmpRemoveWindowLocked(0, imWin); imWin.mTargetAppToken = null; reAddWindowToListInOrderLocked(imWin); + if (DEBUG_INPUT_METHOD) { + Log.v(TAG, "List with no IM target:"); + logWindowList(" "); + } if (DN > 0) moveInputMethodDialogsLocked(-1);; } else { moveInputMethodDialogsLocked(-1);; @@ -1233,7 +1302,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.mSurface != null && !mDisplayFrozen) { // If we are not currently running the exit animation, we // need to see about starting one. - if (wasVisible=win.isVisibleLw()) { + if (wasVisible=win.isWinVisibleLw()) { int transit = WindowManagerPolicy.TRANSIT_EXIT; if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { @@ -1277,6 +1346,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mKeyWaiter.releasePendingPointerLocked(win.mSession); mKeyWaiter.releasePendingTrackballLocked(win.mSession); + win.mRemoved = true; + + if (mInputMethodTarget == win) { + moveInputMethodWindowsIfNeededLocked(false); + } + mPolicy.removeWindowLw(win); win.removeLocked(); @@ -1501,7 +1576,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (win.getAttrs().type == TYPE_APPLICATION_STARTING) { transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE; } - if (win.isVisibleLw() && + if (win.isWinVisibleLw() && applyAnimationLocked(win, transit, false)) { win.mExiting = true; mKeyWaiter.finishedKey(session, client, true, @@ -1511,13 +1586,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // an exit. win.mExiting = true; } else { + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } } } - if (mInputMethodWindow == win) { - mInputMethodWindow = null; - } outSurface.clear(); } @@ -1620,7 +1695,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo private boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { - if (win.mAnimating && win.mAnimationIsEntrance == isEntrance) { + if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. return true; @@ -1666,6 +1741,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo Log.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); + win.mAnimationIsEntrance = isEntrance; } } else { win.clearAnimation(); @@ -2724,7 +2800,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo boolean added = false; for (int j=0; j<NCW; j++) { WindowState cwin = (WindowState)win.mChildWindows.get(j); - if (cwin.mSubLayer >= 0) { + if (!added && cwin.mSubLayer >= 0) { mWindows.add(index, win); index++; added = true; @@ -4645,8 +4721,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo KeyQ() { super(mContext); PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); - mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK - | PowerManager.ON_AFTER_RELEASE, "KEEP_SCREEN_ON_FLAG"); + mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, + "KEEP_SCREEN_ON_FLAG"); mHoldingScreen.setReferenceCounted(false); } @@ -4769,8 +4845,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (holding) { mHoldingScreen.acquire(); } else { - long curTime = SystemClock.uptimeMillis(); - mPowerManager.userActivity(curTime, false, LocalPowerManager.OTHER_EVENT); + mPolicy.screenOnStopped(); mHoldingScreen.release(); } } @@ -5095,6 +5170,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } } + public boolean performHapticFeedback(IWindow window, int effectId, + boolean always) { + synchronized(mWindowMap) { + long ident = Binder.clearCallingIdentity(); + try { + return mPolicy.performHapticFeedback( + windowForClientLocked(this, window), effectId, always); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + void windowAddedLocked() { if (mSurfaceSession == null) { if (localLOGV) Log.v( @@ -5255,9 +5343,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // Currently running animation. boolean mAnimating; + boolean mLocalAnimating; Animation mAnimation; boolean mAnimationIsEntrance; boolean mHasTransformation; + boolean mHasLocalTransformation; final Transformation mTransformation = new Transformation(); // This is set after IWindowSession.relayout() has been called at @@ -5297,6 +5387,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // been updated for the new orientation. boolean mOrientationChanging; + // Is this window now (or just being) removed? + boolean mRemoved; + WindowState(Session s, IWindow c, WindowToken token, WindowState attachedWindow, WindowManager.LayoutParams a, int viewVisibility) { @@ -5516,6 +5609,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (localLOGV) Log.v( TAG, "Setting animation in " + this + ": " + anim); mAnimating = false; + mLocalAnimating = false; mAnimation = anim; mAnimation.restrictDuration(MAX_ANIMATION_DURATION); mAnimation.scaleCurrentDuration(mWindowAnimationScale); @@ -5524,6 +5618,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo public void clearAnimation() { if (mAnimation != null) { mAnimating = true; + mLocalAnimating = false; mAnimation = null; } } @@ -5752,13 +5847,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (!mDrawPending && !mCommitDrawPending && mAnimation != null) { mHasTransformation = true; - if (!mAnimating) { + mHasLocalTransformation = true; + if (!mLocalAnimating) { if (DEBUG_ANIM) Log.v( TAG, "Starting animation in " + this + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mWindowAnimationScale); mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); mAnimation.setStartTime(currentTime); + mLocalAnimating = true; mAnimating = true; } mTransformation.clear(); @@ -5767,9 +5864,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_ANIM) Log.v( TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); - if (mAppToken != null && mAppToken.hasTransformation) { - mTransformation.compose(mAppToken.transformation); - } if (more) { // we're not done! return true; @@ -5780,10 +5874,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo mAnimation = null; //WindowManagerService.this.dump(); } - if (mAppToken != null && mAppToken.hasTransformation) { + mHasLocalTransformation = false; + if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null + && mAppToken.hasTransformation) { + // When our app token is animating, we kind-of pretend like + // we are as well. Note the mLocalAnimating mAnimationIsEntrance + // part of this check means that we will only do this if + // our window is not currently exiting, or it is not + // locally animating itself. The idea being that one that + // is exiting and doing a local animation should be removed + // once that animation is done. mAnimating = true; mHasTransformation = true; - mTransformation.set(mAppToken.transformation); + mTransformation.clear(); return false; } else if (mHasTransformation) { // Little trick to get through the path below to act like @@ -5796,10 +5899,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo // If the display is frozen, and there is a pending animation, // clear it and make sure we run the cleanup code. mAnimating = true; + mLocalAnimating = true; mAnimation = null; } - if (!mAnimating) { + if (!mAnimating && !mLocalAnimating) { return false; } @@ -5809,6 +5913,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + (mAppToken != null ? mAppToken.reportedVisible : false)); mAnimating = false; + mLocalAnimating = false; mAnimation = null; mAnimLayer = mLayer; if (mIsImWindow) { @@ -5817,6 +5922,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this + " anim layer: " + mAnimLayer); mHasTransformation = false; + mHasLocalTransformation = false; mPolicyVisibility = mPolicyVisibilityAfterAnim; mTransformation.clear(); if (mHasDrawn @@ -5891,10 +5997,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } void computeShownFrameLocked() { - final boolean selfTransformation = mHasTransformation; - final boolean attachedTransformation = (mAttachedWindow != null - && mAttachedWindow.mHasTransformation); - if (selfTransformation || attachedTransformation) { + final boolean selfTransformation = mHasLocalTransformation; + Transformation attachedTransformation = + (mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation) + ? mAttachedWindow.mTransformation : null; + Transformation appTransformation = + (mAppToken != null && mAppToken.hasTransformation) + ? mAppToken.transformation : null; + if (selfTransformation || attachedTransformation != null + || appTransformation != null) { // cache often used attributes locally final Rect frame = mFrame; final float tmpFloats[] = mTmpFloats; @@ -5905,8 +6016,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (selfTransformation) { tmpMatrix.preConcat(mTransformation.getMatrix()); } - if (attachedTransformation) { - tmpMatrix.preConcat(mAttachedWindow.mTransformation.getMatrix()); + if (attachedTransformation != null) { + tmpMatrix.preConcat(attachedTransformation.getMatrix()); + } + if (appTransformation != null) { + tmpMatrix.preConcat(appTransformation.getMatrix()); } // "convert" it into SurfaceFlinger's format @@ -5940,8 +6054,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); } - if (attachedTransformation) { - mShownAlpha *= mAttachedWindow.mTransformation.getAlpha(); + if (attachedTransformation != null) { + mShownAlpha *= attachedTransformation.getAlpha(); + } + if (appTransformation != null) { + mShownAlpha *= appTransformation.getAlpha(); } } else { //Log.i(TAG, "Not applying alpha transform"); @@ -5965,7 +6082,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo /** * Is this window visible? It is not visible if there is no * surface, or we are in the process of running an exit animation - * that will remove the surface. + * that will remove the surface, or its app token has been hidden. */ public boolean isVisibleLw() { final AppWindowToken atoken = mAppToken; @@ -5975,6 +6092,18 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo } /** + * Is this window visible, ignoring its app token? It is not visible + * if there is no surface, or we are in the process of running an exit animation + * that will remove the surface. + */ + public boolean isWinVisibleLw() { + final AppWindowToken atoken = mAppToken; + return mSurface != null && mPolicyVisibility && !mAttachedHidden + && (atoken == null || !atoken.hiddenRequested || atoken.animating) + && !mExiting && !mDestroying; + } + + /** * The same as isVisible(), but follows the current hidden state of * the associated app token, not the pending requested hidden state. */ @@ -6203,6 +6332,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo pw.println(prefix + "mShownAlpha=" + mShownAlpha + " mAlpha=" + mAlpha + " mLastAlpha=" + mLastAlpha); pw.println(prefix + "mAnimating=" + mAnimating + + " mLocalAnimating=" + mLocalAnimating + " mAnimationIsEntrance=" + mAnimationIsEntrance + " mAnimation=" + mAnimation); pw.println(prefix + "XForm: has=" + mHasTransformation @@ -6213,7 +6343,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo + " mHasDrawn=" + mHasDrawn); pw.println(prefix + "mExiting=" + mExiting + " mRemoveOnExit=" + mRemoveOnExit - + " mDestroying=" + mDestroying); + + " mDestroying=" + mDestroying + + " mRemoved=" + mRemoved); pw.println(prefix + "mOrientationChanging=" + mOrientationChanging + " mAppFreezing=" + mAppFreezing); } @@ -7959,6 +8090,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo i--; WindowState win = mDestroySurface.get(i); win.mDestroying = false; + if (mInputMethodWindow == win) { + mInputMethodWindow = null; + } win.destroySurfaceLocked(); } while (i > 0); mDestroySurface.clear(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index f5efe4b..e391da3 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -159,7 +159,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final int LOG_AM_BROADCAST_DISCARD_APP = 30025; static final int LOG_AM_CREATE_SERVICE = 30030; static final int LOG_AM_DESTROY_SERVICE = 30031; - + static final int LOG_AM_PROCESS_CRASHED_TOO_MUCH = 30032; + static final int LOG_AM_DROP_PROCESS = 30033; + static final int LOG_AM_SERVICE_CRASHED_TOO_MUCH = 30034; + static final int LOG_AM_SCHEDULE_SERVICE_RESTART = 30035; + static final int LOG_AM_PROVIDER_LOST_PROCESS = 30036; + static final int LOG_BOOT_PROGRESS_AMS_READY = 3040; static final int LOG_BOOT_PROGRESS_ENABLE_SCREEN = 3050; @@ -1787,7 +1792,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - private final void startPausingLocked(boolean userLeaving) { + private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { if (mPausingActivity != null) { RuntimeException e = new RuntimeException(); Log.e(TAG, "Trying to pause when pause is already pending for " @@ -1840,9 +1845,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new - // activity has started... - prev.pauseKeyDispatchingLocked(); - + // activity has started. If we're pausing the activity just because + // the screen is being turned off and the UI is sleeping, don't interrupt + // key dispatch; the same activity will pick it up again on wakeup. + if (!uiSleeping) { + prev.pauseKeyDispatchingLocked(); + } else { + if (DEBUG_PAUSE) Log.v(TAG, "Key dispatch not paused for screen off"); + } + // Schedule a pause timeout in case the app doesn't respond. // We don't give it much time because this directly impacts the // responsiveness seen by the user. @@ -2188,7 +2199,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Log.v(TAG, "Skip resume: need to start pausing"); - startPausingLocked(userLeaving); + startPausingLocked(userLeaving, false); return true; } @@ -2605,8 +2616,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleNewIntent(ar, r); sent = true; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending new intent to " + r, e); + Log.w(TAG, "Exception thrown sending new intent to " + r, e); } } if (!sent) { @@ -3456,7 +3466,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Finish needs to pause: " + r); if (DEBUG_USER_LEAVING) Log.v(TAG, "finish() => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, false); } } else if (r.state != ActivityState.PAUSING) { @@ -3590,9 +3600,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.app.thread.scheduleSendResult(r, list); return; } catch (Exception e) { - Log.w(TAG, - "Exception thrown sending result to " + r, - e); + Log.w(TAG, "Exception thrown sending result to " + r, e); } } @@ -3778,8 +3786,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.configChangeFlags = 0; if (!mLRUActivities.remove(r)) { - Log.w(TAG, "Activity " + r - + " being finished, but not in LRU list"); + Log.w(TAG, "Activity " + r + " being finished, but not in LRU list"); } return removedFromHistory; @@ -4217,7 +4224,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:"+packageName); + Log.w(TAG, "Invalid packageName:" + packageName); return false; } if (uid == pkgUid || checkComponentPermission( @@ -4270,7 +4277,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } catch (RemoteException e) { } if (pkgUid == -1) { - Log.w(TAG, "Invalid packageName:"+packageName); + Log.w(TAG, "Invalid packageName: " + packageName); return; } restartPackageLocked(packageName, pkgUid); @@ -4423,6 +4430,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (app == null) { Log.w(TAG, "No pending application record for pid " + pid + " (IApplicationThread " + thread + "); dropping process"); + EventLog.writeEvent(LOG_AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { Process.killProcess(pid); } else { @@ -6579,10 +6587,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (caller != null) { r = getRecordForAppLocked(caller); if (r == null) { - throw new SecurityException( - "Unable to find app for caller " + caller - + " (pid=" + Binder.getCallingPid() - + ") when getting content provider " + name); + throw new SecurityException( + "Unable to find app for caller " + caller + + " (pid=" + Binder.getCallingPid() + + ") when getting content provider " + name); } } @@ -6739,6 +6747,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + cpi.applicationInfo.packageName + "/" + cpi.applicationInfo.uid + " for provider " + name + ": launching app became null"); + EventLog.writeEvent(LOG_AM_PROVIDER_LOST_PROCESS, + cpi.applicationInfo.packageName, + cpi.applicationInfo.uid, name); + return null; } try { cpr.wait(); @@ -6844,9 +6856,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen ContentProviderRecord dst = (ContentProviderRecord)r.pubProviders.get(src.info.name); if (dst != null) { - dst.provider = src.provider; - dst.app = r; - mProvidersByClass.put(dst.info.name, dst); String names[] = dst.info.authority.split(";"); for (int j = 0; j < names.length; j++) { @@ -6863,6 +6872,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } synchronized (dst) { + dst.provider = src.provider; + dst.app = r; dst.notifyAll(); } updateOomAdjLocked(r); @@ -6993,7 +7004,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (mPausingActivity == null) { if (DEBUG_PAUSE) Log.v(TAG, "Sleep needs to pause..."); if (DEBUG_USER_LEAVING) Log.v(TAG, "Sleep => pause with userLeaving=false"); - startPausingLocked(false); + startPausingLocked(false, true); } } } @@ -7499,6 +7510,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // This process loses! Log.w(TAG, "Process " + app.info.processName + " has crashed too many times: killing!"); + EventLog.writeEvent(LOG_AM_PROCESS_CRASHED_TOO_MUCH, + app.info.processName, app.info.uid); killServicesLocked(app, false); for (int i=mHistory.size()-1; i>=0; i--) { HistoryRecord r = (HistoryRecord)mHistory.get(i); @@ -8429,6 +8442,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (sr.crashCount >= 2) { Log.w(TAG, "Service crashed " + sr.crashCount + " times, stopping: " + sr); + EventLog.writeEvent(LOG_AM_SERVICE_CRASHED_TOO_MUCH, + sr.crashCount, sr.shortName, app.pid); bringDownServiceLocked(sr, true); } else if (!allowRestart) { bringDownServiceLocked(sr, true); @@ -8447,7 +8462,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private final void removeDyingProviderLocked(ProcessRecord proc, ContentProviderRecord cpr) { - cpr.launchingApp = null; + synchronized (cpr) { + cpr.launchingApp = null; + cpr.notifyAll(); + } mProvidersByClass.remove(cpr.info.name); String names[] = cpr.info.authority.split(";"); @@ -8529,6 +8547,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen break; } } + } else { + i = NL; } if (i >= NL) { @@ -8602,7 +8622,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mProcessesOnHold.remove(app); if (restart) { - // We have component that still need to be running in the + // We have components that still need to be running in the // process, so re-launch it. mProcessNames.put(app.processName, app.info.uid, app); startProcessLocked(app, "restart", app.processName); @@ -8952,6 +8972,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay; Log.w(TAG, "Scheduling restart of crashed service " + r.shortName + " in " + r.restartDelay + "ms"); + EventLog.writeEvent(LOG_AM_SCHEDULE_SERVICE_RESTART, + r.shortName, r.restartDelay); Message msg = Message.obtain(); msg.what = SERVICE_ERROR_MSG; @@ -11660,7 +11682,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // We can finish this one if we have its icicle saved and // it is not persistent. - if ((r.haveState || !r.stateNotNeeded) + if ((r.haveState || !r.stateNotNeeded) && !r.visible && r.stopped && !r.persistent && !r.finishing) { final int origSize = mLRUActivities.size(); destroyActivityLocked(r, true); diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 001987f..3922f39 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -22,13 +22,24 @@ import android.content.Context; import android.os.Binder; import android.os.IBinder; import com.android.internal.os.PkgUsageStats; +import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.util.Log; +import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; @@ -44,35 +55,257 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; static IUsageStats sService; private Context mContext; - private String mFileName; + // structure used to maintain statistics since the last checkin. final private Map<String, PkgUsageStatsExtended> mStats; + // Lock to update package stats. Methods suffixed by SLOCK should invoked with + // this lock held + final Object mStatsLock; + // Lock to write to file. Methods suffixed by FLOCK should invoked with + // this lock held. + final Object mFileLock; + // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks private String mResumedPkg; + private File mFile; + //private File mBackupFile; + private long mLastWriteRealTime; + private int _FILE_WRITE_INTERVAL = 30*60*1000; //ms + private static final String _PREFIX_DELIMIT="."; + private String mFilePrefix; + private Calendar mCal; + private static final int _MAX_NUM_FILES = 10; + private long mLastTime; private class PkgUsageStatsExtended { int mLaunchCount; long mUsageTime; - long mChgTime; + long mPausedTime; + long mResumedTime; + PkgUsageStatsExtended() { mLaunchCount = 0; mUsageTime = 0; - mChgTime = SystemClock.elapsedRealtime(); } void updateResume() { mLaunchCount ++; - mChgTime = SystemClock.elapsedRealtime(); + mResumedTime = SystemClock.elapsedRealtime(); } void updatePause() { - long currTime = SystemClock.elapsedRealtime(); - mUsageTime += (currTime - mChgTime); - mChgTime = currTime; + mPausedTime = SystemClock.elapsedRealtime(); + mUsageTime += (mPausedTime - mResumedTime); + } + void clear() { + mLaunchCount = 0; + mUsageTime = 0; } } - UsageStatsService(String filename) { - mFileName = filename; + UsageStatsService(String fileName) { mStats = new HashMap<String, PkgUsageStatsExtended>(); + mStatsLock = new Object(); + mFileLock = new Object(); + mFilePrefix = fileName; + mCal = Calendar.getInstance(); + // Update current stats which are binned by date + String uFileName = getCurrentDateStr(mFilePrefix); + mFile = new File(uFileName); + readStatsFromFile(); + mLastWriteRealTime = SystemClock.elapsedRealtime(); + mLastTime = new Date().getTime(); + } + + /* + * Utility method to convert date into string. + */ + private String getCurrentDateStr(String prefix) { + mCal.setTime(new Date()); + StringBuilder sb = new StringBuilder(); + if (prefix != null) { + sb.append(prefix); + sb.append("."); + } + int mm = mCal.get(Calendar.MONTH) - Calendar.JANUARY +1; + if (mm < 10) { + sb.append("0"); + } + sb.append(mm); + int dd = mCal.get(Calendar.DAY_OF_MONTH); + if (dd < 10) { + sb.append("0"); + } + sb.append(dd); + sb.append(mCal.get(Calendar.YEAR)); + return sb.toString(); + } + + private Parcel getParcelForFile(File file) throws IOException { + FileInputStream stream = new FileInputStream(file); + byte[] raw = readFully(stream); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, raw.length); + in.setDataPosition(0); + stream.close(); + return in; + } + + private void readStatsFromFile() { + File newFile = mFile; + synchronized (mFileLock) { + try { + if (newFile.exists()) { + readStatsFLOCK(newFile); + } else { + // Check for file limit before creating a new file + checkFileLimitFLOCK(); + newFile.createNewFile(); + } + } catch (IOException e) { + Log.w(TAG,"Error : " + e + " reading data from file:" + newFile); + } + } + } + + private void readStatsFLOCK(File file) throws IOException { + Parcel in = getParcelForFile(file); + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + PkgUsageStatsExtended pus = new PkgUsageStatsExtended(); + pus.mLaunchCount = in.readInt(); + pus.mUsageTime = in.readLong(); + synchronized (mStatsLock) { + mStats.put(pkgName, pus); + } + } + } + + private ArrayList<String> getUsageStatsFileListFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return null; + } + // Check if there are too many files in the system and delete older files + String fList[] = dir.list(); + if (fList == null) { + return null; + } + File pre = new File(mFilePrefix); + String filePrefix = pre.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + ArrayList<String> fileList = new ArrayList<String>(); + for (String file : fList) { + int index = file.indexOf(filePrefix); + if (index == -1) { + continue; + } + if (file.endsWith(".bak")) { + continue; + } + fileList.add(file); + } + return fileList; + } + + private File getUsageFilesDir() { + if (mFilePrefix == null) { + return null; + } + File pre = new File(mFilePrefix); + return new File(pre.getParent()); } + private void checkFileLimitFLOCK() { + File dir = getUsageFilesDir(); + if (dir == null) { + Log.w(TAG, "Couldnt find writable directory for usage stats file"); + return; + } + // Get all usage stats output files + ArrayList<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + // Strange but we dont have to delete any thing + return; + } + int count = fileList.size(); + if (count <= _MAX_NUM_FILES) { + return; + } + // Sort files + Collections.sort(fileList); + count -= _MAX_NUM_FILES; + // Delete older files + for (int i = 0; i < count; i++) { + String fileName = fileList.get(i); + File file = new File(dir, fileName); + Log.i(TAG, "Deleting file : "+fileName); + file.delete(); + } + } + + private void writeStatsToFile() { + synchronized (mFileLock) { + long currTime = new Date().getTime(); + boolean dayChanged = ((currTime - mLastTime) >= (24*60*60*1000)); + long currRealTime = SystemClock.elapsedRealtime(); + if (((currRealTime-mLastWriteRealTime) < _FILE_WRITE_INTERVAL) && + (!dayChanged)) { + // wait till the next update + return; + } + // Get the most recent file + String todayStr = getCurrentDateStr(mFilePrefix); + // Copy current file to back up + File backupFile = new File(mFile.getPath() + ".bak"); + mFile.renameTo(backupFile); + try { + checkFileLimitFLOCK(); + mFile.createNewFile(); + // Write mStats to file + writeStatsFLOCK(); + mLastWriteRealTime = currRealTime; + mLastTime = currTime; + if (dayChanged) { + // clear stats + synchronized (mStats) { + mStats.clear(); + } + mFile = new File(todayStr); + } + // Delete the backup file + if (backupFile != null) { + backupFile.delete(); + } + } catch (IOException e) { + Log.w(TAG, "Failed writing stats to file:" + mFile); + if (backupFile != null) { + backupFile.renameTo(mFile); + } + } + } + } + + private void writeStatsFLOCK() throws IOException { + FileOutputStream stream = new FileOutputStream(mFile); + Parcel out = Parcel.obtain(); + writeStatsToParcelFLOCK(out); + stream.write(out.marshall()); + out.recycle(); + stream.flush(); + stream.close(); + } + + private void writeStatsToParcelFLOCK(Parcel out) { + synchronized (mStatsLock) { + Set<String> keys = mStats.keySet(); + for (String key : keys) { + PkgUsageStatsExtended pus = mStats.get(key); + out.writeString(key); + out.writeInt(pus.mLaunchCount); + out.writeLong(pus.mUsageTime); + } + } + } + public void publish(Context context) { mContext = context; ServiceManager.addService(SERVICE_NAME, asBinder()); @@ -99,12 +332,14 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } if (localLOGV) Log.i(TAG, "started component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - pus = new PkgUsageStatsExtended(); - mStats.put(pkgName, pus); + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + pus = new PkgUsageStatsExtended(); + mStats.put(pkgName, pus); + } + pus.updateResume(); } - pus.updateResume(); mResumedPkg = pkgName; } @@ -120,13 +355,17 @@ public final class UsageStatsService extends IUsageStats.Stub { return; } if (localLOGV) Log.i(TAG, "paused component:"+pkgName); - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - // Weird some error here - Log.w(TAG, "No package stats for pkg:"+pkgName); - return; + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + // Weird some error here + Log.w(TAG, "No package stats for pkg:"+pkgName); + return; + } + pus.updatePause(); } - pus.updatePause(); + // Persist data to file + writeStatsToFile(); } public void enforceCallingPermission() { @@ -145,17 +384,19 @@ public final class UsageStatsService extends IUsageStats.Stub { ((pkgName = componentName.getPackageName()) == null)) { return null; } - PkgUsageStatsExtended pus = mStats.get(pkgName); - if (pus == null) { - return null; + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus == null) { + return null; + } + return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); } - return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime); } public PkgUsageStats[] getAllPkgUsageStats() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); - synchronized (mStats) { + synchronized (mStatsLock) { Set<String> keys = mStats.keySet(); int size = keys.size(); if (size <= 0) { @@ -172,21 +413,120 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + static byte[] readFully(FileInputStream stream) throws java.io.IOException { + int pos = 0; + int avail = stream.available(); + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + if (amt <= 0) { + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length-pos) { + byte[] newData = new byte[pos+avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + private void collectDumpInfoFLOCK(PrintWriter pw, String[] args) { + List<String> fileList = getUsageStatsFileListFLOCK(); + if (fileList == null) { + return; + } + final boolean isCheckinRequest = scanArgs(args, "-c"); + Collections.sort(fileList); + File usageFile = new File(mFilePrefix); + String dirName = usageFile.getParent(); + File dir = new File(dirName); + String filePrefix = usageFile.getName(); + // file name followed by dot + int prefixLen = filePrefix.length()+1; + String todayStr = getCurrentDateStr(null); + for (String file : fileList) { + File dFile = new File(dir, file); + String dateStr = file.substring(prefixLen); + try { + Parcel in = getParcelForFile(dFile); + collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCheckinRequest); + if (isCheckinRequest && !todayStr.equalsIgnoreCase(dateStr)) { + // Delete old file after collecting info only for checkin requests + dFile.delete(); + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : " + file); + return; + } catch (IOException e) { + Log.w(TAG, "Failed with "+e+" when collecting dump info from file : "+file); + } + } + } + + private void collectDumpInfoFromParcelFLOCK(Parcel in, PrintWriter pw, + String date, boolean isCheckinRequest) { StringBuilder sb = new StringBuilder(); - synchronized (mStats) { - Set<String> keys = mStats.keySet(); - for (String key: keys) { - PkgUsageStatsExtended ps = mStats.get(key); - sb.append("pkg="); - sb.append(key); + sb.append("Date:"); + sb.append(date); + boolean first = true; + while (in.dataAvail() > 0) { + String pkgName = in.readString(); + int launchCount = in.readInt(); + long usageTime = in.readLong(); + if (isCheckinRequest) { + if (!first) { + sb.append(","); + } + sb.append(pkgName); + sb.append(","); + sb.append(launchCount); + sb.append(","); + sb.append(usageTime); + sb.append("ms"); + } else { + if (first) { + sb.append("\n"); + } + sb.append("pkg="); + sb.append(pkgName); sb.append(", launchCount="); - sb.append(ps.mLaunchCount); + sb.append(launchCount); sb.append(", usageTime="); - sb.append(ps.mUsageTime+" ms\n"); + sb.append(usageTime); + sb.append(" ms\n"); } + first = false; } pw.write(sb.toString()); } + + /** + * Searches array of arguments for the specified string + * @param args array of argument strings + * @param value value to search for + * @return true if the value is contained in the array + */ + private static boolean scanArgs(String[] args, String value) { + if (args != null) { + for (String arg : args) { + if (value.equals(arg)) { + return true; + } + } + } + return false; + } + + @Override + /* + * The data persisted to file is parsed and the stats are computed. + */ + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mFileLock) { + collectDumpInfoFLOCK(pw, args); + } + } + } |