diff options
Diffstat (limited to 'services')
78 files changed, 1944 insertions, 1025 deletions
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index bfbf0ac..2781890 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1040,7 +1040,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void addServiceLocked(Service service, UserState userState) { try { - service.linkToOwnDeathLocked(); + service.onAdded(); userState.mBoundServices.add(service); userState.mComponentNameToServiceMap.put(service.mComponentName, service); } catch (RemoteException re) { @@ -1056,7 +1056,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void removeServiceLocked(Service service, UserState userState) { userState.mBoundServices.remove(service); userState.mComponentNameToServiceMap.remove(service.mComponentName); - service.unlinkToOwnDeathLocked(); + service.onRemoved(); } /** @@ -1931,6 +1931,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final ResolveInfo mResolveInfo; + final IBinder mOverlayWindowToken = new Binder(); + // the events pending events to be dispatched to this service final SparseArray<AccessibilityEvent> mPendingEvents = new SparseArray<>(); @@ -2112,7 +2114,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mBindingServices.remove(mComponentName); mWasConnectedAndDied = false; try { - mServiceInterface.setConnection(this, mId); + mServiceInterface.init(this, mId, mOverlayWindowToken); onUserStateChangedLocked(userState); } catch (RemoteException re) { Slog.w(LOG_TAG, "Error while setting connection for service: " @@ -2602,6 +2604,27 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { /* do nothing - #binderDied takes care */ } + public void onAdded() throws RemoteException { + linkToOwnDeathLocked(); + final long identity = Binder.clearCallingIdentity(); + try { + mWindowManagerService.addWindowToken(mOverlayWindowToken, + WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public void onRemoved() { + final long identity = Binder.clearCallingIdentity(); + try { + mWindowManagerService.removeWindowToken(mOverlayWindowToken, true); + } finally { + Binder.restoreCallingIdentity(identity); + } + unlinkToOwnDeathLocked(); + } + public void linkToOwnDeathLocked() throws RemoteException { mService.linkToDeath(this, 0); } @@ -2614,7 +2637,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { try { // Clear the proxy in the other process so this // IAccessibilityServiceConnection can be garbage collected. - mServiceInterface.setConnection(null, mId); + mServiceInterface.init(null, mId, null); } catch (RemoteException re) { /* ignore */ } @@ -3156,7 +3179,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case WindowManager.LayoutParams.TYPE_STATUS_BAR: case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL: case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL: - case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: @@ -3165,6 +3187,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return AccessibilityWindowInfo.TYPE_SYSTEM; } + case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY: { + return AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY; + } + default: { return -1; } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6a6dcaf..bf67461 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -238,8 +238,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean mLockdownEnabled; private LockdownVpnTracker mLockdownTracker; - private Nat464Xlat mClat; - /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */ private Object mRulesLock = new Object(); /** Currently active network rules by UID. */ @@ -715,12 +713,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { intentFilter.addAction(Intent.ACTION_USER_STOPPING); mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); - mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler); try { mNetd.registerObserver(mTethering); mNetd.registerObserver(mDataActivityObserver); - mNetd.registerObserver(mClat); } catch (RemoteException e) { loge("Error registering observer :" + e); } @@ -3549,7 +3545,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before // we do anything else, make sure its LinkProperties are accurate. - mClat.fixupLinkProperties(networkAgent, oldLp); + if (networkAgent.clatd != null) { + networkAgent.clatd.fixupLinkProperties(oldLp); + } updateInterfaces(newLp, oldLp, netId); updateMtu(newLp, oldLp); @@ -3568,15 +3566,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo na) { - final boolean wasRunningClat = mClat.isRunningClat(na); - final boolean shouldRunClat = Nat464Xlat.requiresClat(na); + private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) { + final boolean wasRunningClat = nai.clatd != null && nai.clatd.isStarted(); + final boolean shouldRunClat = Nat464Xlat.requiresClat(nai); if (!wasRunningClat && shouldRunClat) { - // Start clatd. If it's already been started but is not running yet, this is a no-op. - mClat.startClat(na); + nai.clatd = new Nat464Xlat(mContext, mNetd, mTrackerHandler, nai); + nai.clatd.start(); } else if (wasRunningClat && !shouldRunClat) { - mClat.stopClat(); + nai.clatd.stop(); } } diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 64d8f6f..8417ccc 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -52,7 +52,7 @@ option java_package com.android.server # NotificationManagerService.java # --------------------------- # when a NotificationManager.notify is called -2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3) +2750 notification_enqueue (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(notification|3),(update|1) # when someone tries to cancel a notification, the notification manager sometimes # calls this with flags too 2751 notification_cancel (uid|1|5),(pid|1|5),(pkg|3),(id|1|5),(tag|3),(userid|1|5),(required_flags|1),(forbidden_flags|1),(reason|1|5),(listener|3) @@ -69,6 +69,10 @@ option java_package com.android.server 27511 notification_expansion (key|3),(user_action|1),(expanded|1) # when a notification has been clicked 27520 notification_clicked (key|3) +# when a notification action button has been clicked +27521 notification_action_clicked (key|3),(action_index|1) +# when a notification has been canceled +27530 notification_canceled (key|3),(reason|1) # --------------------------- # Watchdog.java diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 1623eac..edea274 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -774,7 +774,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (defIm == null && mMethodList.size() > 0) { defIm = InputMethodUtils.getMostApplicableDefaultIME( mSettings.getEnabledInputMethodListLocked()); - Slog.i(TAG, "No default found, using " + defIm.getId()); + if (defIm != null) { + Slog.i(TAG, "Default found, using " + defIm.getId()); + } else { + Slog.i(TAG, "No default found"); + } } if (defIm != null) { setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false); diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 07cc864..387eabc 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -30,6 +29,7 @@ import java.util.Set; import android.net.Uri; import android.util.FastImmutableArraySet; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -736,7 +736,7 @@ public abstract class IntentResolver<F extends IntentFilter, R extends Object> { /** * All filters that have been registered. */ - private final HashSet<F> mFilters = new HashSet<F>(); + private final ArraySet<F> mFilters = new ArraySet<F>(); /** * All of the MIME types that have been registered, such as "image/jpeg", diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 28a6917..d9c96e4 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1796,9 +1796,6 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGpsStatusListener(IGpsStatusListener listener, String packageName) { - if (mGpsStatusProvider == null) { - return false; - } int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, LocationManager.GPS_PROVIDER); @@ -1813,6 +1810,10 @@ public class LocationManagerService extends ILocationManager.Stub { Binder.restoreCallingIdentity(ident); } + if (mGpsStatusProvider == null) { + return false; + } + try { mGpsStatusProvider.addGpsStatusListener(listener); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java index 926235f..9596b57 100644 --- a/services/core/java/com/android/server/MmsServiceBroker.java +++ b/services/core/java/com/android/server/MmsServiceBroker.java @@ -219,7 +219,7 @@ public class MmsServiceBroker extends SystemService { // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service" private final class BinderService extends IMms.Stub { @Override - public void sendMessage(long subId, String callingPkg, Uri contentUri, + public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); @@ -232,7 +232,7 @@ public class MmsServiceBroker extends SystemService { } @Override - public void downloadMessage(long subId, String callingPkg, String locationUrl, + public void downloadMessage(int subId, String callingPkg, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent) throws RemoteException { mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, @@ -259,7 +259,7 @@ public class MmsServiceBroker extends SystemService { } @Override - public Bundle getCarrierConfigValues(long subId) throws RemoteException { + public Bundle getCarrierConfigValues(int subId) throws RemoteException { return getServiceGuarded().getCarrierConfigValues(subId); } @@ -360,7 +360,7 @@ public class MmsServiceBroker extends SystemService { } @Override - public void sendStoredMessage(long subId, String callingPkg, Uri messageUri, + public void sendStoredMessage(int subId, String callingPkg, Uri messageUri, Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send stored MMS message"); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 020c951..0f033d7 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -940,6 +940,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override + public void setInterfaceIpv6NdOffload(String iface, boolean enable) { + mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); + try { + mConnector.execute( + "interface", "ipv6ndoffload", iface, (enable ? "enable" : "disable")); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override public void addRoute(int netId, RouteInfo route) { modifyRoute("add", "" + netId, route); } @@ -1854,23 +1865,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub } @Override - public void stopClatd() throws IllegalStateException { + public void stopClatd(String interfaceName) throws IllegalStateException { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("clatd", "stop"); + mConnector.execute("clatd", "stop", interfaceName); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } } @Override - public boolean isClatdStarted() { + public boolean isClatdStarted(String interfaceName) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); final NativeDaemonEvent event; try { - event = mConnector.execute("clatd", "status"); + event = mConnector.execute("clatd", "status", interfaceName); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index cf2a49f..f4fb519 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -32,8 +32,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; -import java.util.HashMap; -import java.util.HashSet; import static com.android.internal.util.ArrayUtils.appendInt; @@ -50,7 +48,7 @@ public class SystemConfig { // These are the built-in uid -> permission mappings that were read from the // system configuration files. - final SparseArray<HashSet<String>> mSystemPermissions = new SparseArray<>(); + final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>(); // These are the built-in shared libraries that were read from the // system configuration files. Keys are the library names; strings are the @@ -59,7 +57,7 @@ public class SystemConfig { // These are the features this devices supports that were read from the // system configuration files. - final HashMap<String, FeatureInfo> mAvailableFeatures = new HashMap<>(); + final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>(); public static final class PermissionEntry { public final String name; @@ -94,7 +92,7 @@ public class SystemConfig { return mGlobalGids; } - public SparseArray<HashSet<String>> getSystemPermissions() { + public SparseArray<ArraySet<String>> getSystemPermissions() { return mSystemPermissions; } @@ -102,7 +100,7 @@ public class SystemConfig { return mSharedLibraries; } - public HashMap<String, FeatureInfo> getAvailableFeatures() { + public ArrayMap<String, FeatureInfo> getAvailableFeatures() { return mAvailableFeatures; } @@ -252,9 +250,9 @@ public class SystemConfig { continue; } perm = perm.intern(); - HashSet<String> perms = mSystemPermissions.get(uid); + ArraySet<String> perms = mSystemPermissions.get(uid); if (perms == null) { - perms = new HashSet<String>(); + perms = new ArraySet<String>(); mSystemPermissions.put(uid, perms); } perms.add(perm); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index b3337bb..fcc5339 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -72,7 +72,7 @@ import com.android.server.am.BatteryStatsService; * and 15973975 by saving the phoneId of the registrant and then using the * phoneId when deciding to to make a callback. This is necessary because * a subId changes from to a dummy value when a SIM is removed and thus won't - * compare properly. Because SubscriptionManager.getPhoneId(long subId) handles + * compare properly. Because SubscriptionManager.getPhoneId(int subId) handles * the dummy value conversion we properly do the callbacks. * * Eventually we may want to remove the notion of dummy value but for now this @@ -95,7 +95,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { int events; - long subId; + int subId; int phoneId; @@ -154,7 +154,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private VoLteServiceState mVoLteServiceState = new VoLteServiceState(); - private long mDefaultSubId = SubscriptionManager.INVALID_SUB_ID; + private int mDefaultSubId = SubscriptionManager.INVALID_SUB_ID; private int mDefaultPhoneId = SubscriptionManager.INVALID_PHONE_ID; @@ -201,7 +201,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } case MSG_UPDATE_DEFAULT_SUB: { int newDefaultPhoneId = msg.arg1; - long newDefaultSubId = (Long)(msg.obj); + int newDefaultSubId = (Integer)(msg.obj); if (VDBG) { log("MSG_UPDATE_DEFAULT_SUB:current mDefaultSubId=" + mDefaultSubId + " current mDefaultPhoneId=" + mDefaultPhoneId + " newDefaultSubId= " @@ -212,11 +212,12 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { //defaultSubId comes before new defaultSubId update) we need to recall all //possible missed notify callback synchronized (mRecords) { - for (Record r : mRecords) { - if(r.subId == SubscriptionManager.DEFAULT_SUB_ID) { - checkPossibleMissNotify(r, newDefaultPhoneId); - } - } + for (Record r : mRecords) { + if(r.subId == SubscriptionManager.DEFAULT_SUB_ID) { + checkPossibleMissNotify(r, newDefaultPhoneId); + } + } + handleRemoveListLocked(); } mDefaultSubId = newDefaultSubId; mDefaultPhoneId = newDefaultPhoneId; @@ -235,7 +236,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { if (DBG) log("onReceive: userHandle=" + userHandle); mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0)); } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) { - Long newDefaultSubIdObj = new Long(intent.getLongExtra( + Integer newDefaultSubIdObj = new Integer(intent.getIntExtra( PhoneConstants.SUBSCRIPTION_KEY, SubscriptionManager.getDefaultSubId())); int newDefaultPhoneId = intent.getIntExtra(PhoneConstants.SLOT_KEY, SubscriptionManager.getPhoneId(mDefaultSubId)); @@ -331,13 +332,13 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void listenForSubscriber(long subId, String pkgForDebug, IPhoneStateListener callback, + public void listenForSubscriber(int subId, String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { listen(pkgForDebug, callback, events, notifyNow, subId); } private void listen(String pkgForDebug, IPhoneStateListener callback, int events, - boolean notifyNow, long subId) { + boolean notifyNow, int subId) { int callerUid = UserHandle.getCallingUserId(); int myUid = UserHandle.myUserId(); if (VDBG) { @@ -345,10 +346,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { + " notifyNow=" + notifyNow + " subId=" + subId + " myUid=" + myUid + " callerUid=" + callerUid); } - if (events != 0) { + + if (events != PhoneStateListener.LISTEN_NONE) { /* Checks permission and throws Security exception */ checkListenerPermission(events); - synchronized (mRecords) { // register Record r = null; @@ -363,26 +364,26 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } r = new Record(); r.binder = b; - r.callback = callback; - r.pkgForDebug = pkgForDebug; - r.callerUid = callerUid; - // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, - // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID - if (!SubscriptionManager.isValidSubId(subId)) { - r.subId = SubscriptionManager.DEFAULT_SUB_ID; - } else {//APP specify subID - r.subId = subId; - } - r.phoneId = SubscriptionManager.getPhoneId(r.subId); - mRecords.add(r); if (DBG) log("listen: add new record"); } + r.callback = callback; + r.pkgForDebug = pkgForDebug; + r.callerUid = callerUid; + // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID, + // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID + if (!SubscriptionManager.isValidSubId(subId)) { + r.subId = SubscriptionManager.DEFAULT_SUB_ID; + } else {//APP specify subID + r.subId = subId; + } + r.phoneId = SubscriptionManager.getPhoneId(r.subId); + int phoneId = r.phoneId; r.events = events; if (DBG) { - log("listen: r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); + log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId); } if (VDBG) toStringLogSSC("listen"); if (notifyNow && validatePhoneId(phoneId)) { @@ -502,6 +503,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } } else { + if(DBG) log("listen: Unregister"); remove(callback.asBinder()); } } @@ -543,7 +545,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastCallStateChanged(state, incomingNumber, SubscriptionManager.DEFAULT_SUB_ID); } - public void notifyCallStateForSubscriber(long subId, int state, String incomingNumber) { + public void notifyCallStateForSubscriber(int subId, int state, String incomingNumber) { if (!checkNotifyPermission("notifyCallState()")) { return; } @@ -573,7 +575,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastCallStateChanged(state, incomingNumber, subId); } - public void notifyServiceStateForPhoneId(int phoneId, long subId, ServiceState state) { + public void notifyServiceStateForPhoneId(int phoneId, int subId, ServiceState state) { if (!checkNotifyPermission("notifyServiceState()")){ return; } @@ -619,7 +621,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { notifySignalStrengthForSubscriber(SubscriptionManager.DEFAULT_SUB_ID, signalStrength); } - public void notifySignalStrengthForSubscriber(long subId, SignalStrength signalStrength) { + public void notifySignalStrengthForSubscriber(int subId, SignalStrength signalStrength) { if (!checkNotifyPermission("notifySignalStrength()")) { return; } @@ -679,7 +681,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { notifyCellInfoForSubscriber(SubscriptionManager.DEFAULT_SUB_ID, cellInfo); } - public void notifyCellInfoForSubscriber(long subId, List<CellInfo> cellInfo) { + public void notifyCellInfoForSubscriber(int subId, List<CellInfo> cellInfo) { if (!checkNotifyPermission("notifyCellInfo()")) { return; } @@ -736,7 +738,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyMessageWaitingChangedForPhoneId(int phoneId, long subId, boolean mwi) { + public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) { if (!checkNotifyPermission("notifyMessageWaitingChanged()")) { return; } @@ -766,7 +768,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { notifyCallForwardingChangedForSubscriber(SubscriptionManager.DEFAULT_SUB_ID, cfi); } - public void notifyCallForwardingChangedForSubscriber(long subId, boolean cfi) { + public void notifyCallForwardingChangedForSubscriber(int subId, boolean cfi) { if (!checkNotifyPermission("notifyCallForwardingChanged()")) { return; } @@ -797,7 +799,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { notifyDataActivityForSubscriber(SubscriptionManager.DEFAULT_SUB_ID, state); } - public void notifyDataActivityForSubscriber(long subId, int state) { + public void notifyDataActivityForSubscriber(int subId, int state) { if (!checkNotifyPermission("notifyDataActivity()" )) { return; } @@ -825,7 +827,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { networkCapabilities, networkType, roaming); } - public void notifyDataConnectionForSubscriber(long subId, int state, + public void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataConnectivityPossible, String reason, String apn, String apnType, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int networkType, boolean roaming) { @@ -914,7 +916,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { reason, apnType); } - public void notifyDataConnectionFailedForSubscriber(long subId, + public void notifyDataConnectionFailedForSubscriber(int subId, String reason, String apnType) { if (!checkNotifyPermission("notifyDataConnectionFailed()")) { return; @@ -947,7 +949,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { notifyCellLocationForSubscriber(SubscriptionManager.DEFAULT_SUB_ID, cellLocation); } - public void notifyCellLocationForSubscriber(long subId, Bundle cellLocation) { + public void notifyCellLocationForSubscriber(int subId, Bundle cellLocation) { log("notifyCellLocationForSubscriber: subId=" + subId + " cellLocation=" + cellLocation); if (!checkNotifyPermission("notifyCellLocation()")) { @@ -1094,7 +1096,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyOemHookRawEventForSubscriber(long subId, byte[] rawData) { + public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) { if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) { return; } @@ -1160,7 +1162,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { // the legacy intent broadcasting // - private void broadcastServiceStateChanged(ServiceState state, long subId) { + private void broadcastServiceStateChanged(ServiceState state, int subId) { long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneState(state.getState()); @@ -1179,7 +1181,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private void broadcastSignalStrengthChanged(SignalStrength signalStrength, long subId) { + private void broadcastSignalStrengthChanged(SignalStrength signalStrength, int subId) { long ident = Binder.clearCallingIdentity(); try { mBatteryStats.notePhoneSignalStrength(signalStrength); @@ -1198,7 +1200,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } - private void broadcastCallStateChanged(int state, String incomingNumber, long subId) { + private void broadcastCallStateChanged(int state, String incomingNumber, int subId) { long ident = Binder.clearCallingIdentity(); try { if (state == TelephonyManager.CALL_STATE_IDLE) { @@ -1226,7 +1228,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible, String reason, String apn, String apnType, LinkProperties linkProperties, - NetworkCapabilities networkCapabilities, boolean roaming, long subId) { + NetworkCapabilities networkCapabilities, boolean roaming, int subId) { // Note: not reporting to the battery stats service here, because the // status bar takes care of that after taking into account all of the // required info. @@ -1258,7 +1260,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void broadcastDataConnectionFailed(String reason, String apnType, - long subId) { + int subId) { Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED); intent.putExtra(PhoneConstants.FAILURE_REASON_KEY, reason); intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType); @@ -1374,11 +1376,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private static class LogSSC { private Time mTime; private String mS; - private long mSubId; + private int mSubId; private int mPhoneId; private ServiceState mState; - public void set(Time t, String s, long subId, int phoneId, ServiceState state) { + public void set(Time t, String s, int subId, int phoneId, ServiceState state) { mTime = t; mS = s; mSubId = subId; mPhoneId = phoneId; mState = state; } @@ -1391,7 +1393,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private LogSSC logSSC [] = new LogSSC[10]; private int next = 0; - private void logServiceStateChanged(String s, long subId, int phoneId, ServiceState state) { + private void logServiceStateChanged(String s, int subId, int phoneId, ServiceState state) { if (logSSC == null || logSSC.length == 0) { return; } @@ -1427,7 +1429,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - boolean subIdMatch(long rSubId, long subId) { + boolean subIdMatch(int rSubId, int subId) { if(rSubId == SubscriptionManager.DEFAULT_SUB_ID) { return (subId == mDefaultSubId); } else { @@ -1445,7 +1447,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onServiceStateChanged( new ServiceState(mServiceState[phoneId])); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } @@ -1472,7 +1474,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1 : gsmSignalStrength)); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } @@ -1484,7 +1486,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } @@ -1497,7 +1499,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onMessageWaitingIndicatorChanged( mMessageWaiting[phoneId]); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } @@ -1510,7 +1512,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onCallForwardingIndicatorChanged( mCallForwarding[phoneId]); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } @@ -1535,7 +1537,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId], mDataConnectionNetworkType[phoneId]); } catch (RemoteException ex) { - remove(r.binder); + mRemoveList.add(r.binder); } } } diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java index d4c436f..5add88e 100644 --- a/services/core/java/com/android/server/TextServicesManagerService.java +++ b/services/core/java/com/android/server/TextServicesManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.textservice.ISpellCheckerService; import com.android.internal.textservice.ISpellCheckerSession; @@ -28,14 +29,18 @@ import org.xmlpull.v1.XmlPullParserException; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.IUserSwitchObserver; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -43,6 +48,7 @@ import android.os.IRemoteCallback; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.service.textservice.SpellCheckerService; import android.text.TextUtils; @@ -84,6 +90,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { public TextServicesManagerService(Context context) { mSystemReady = false; mContext = context; + + final IntentFilter broadcastFilter = new IntentFilter(); + broadcastFilter.addAction(Intent.ACTION_USER_ADDED); + broadcastFilter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiver(new TextServicesBroadcastReceiver(), broadcastFilter); + int userId = UserHandle.USER_OWNER; try { ActivityManagerNative.getDefault().registerUserSwitchObserver( @@ -119,6 +131,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private void switchUserLocked(int userId) { mSettings.setCurrentUserId(userId); + updateCurrentProfileIds(); unbindServiceLocked(); buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); SpellCheckerInfo sci = getCurrentSpellChecker(null); @@ -133,6 +146,16 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + void updateCurrentProfileIds() { + List<UserInfo> profiles = + UserManager.get(mContext).getProfiles(mSettings.getCurrentUserId()); + int[] currentProfileIds = new int[profiles.size()]; // profiles will not be null + for (int i = 0; i < currentProfileIds.length; i++) { + currentProfileIds[i] = profiles.get(i).id; + } + mSettings.setCurrentProfileIds(currentProfileIds); + } + private class TextServicesMonitor extends PackageMonitor { private boolean isChangingPackagesOfCurrentUser() { final int userId = getChangingUserId(); @@ -171,6 +194,19 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } } + class TextServicesBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_USER_ADDED.equals(action) + || Intent.ACTION_USER_REMOVED.equals(action)) { + updateCurrentProfileIds(); + return; + } + Slog.w(TAG, "Unexpected intent " + intent); + } + } + private static void buildSpellCheckerMapLocked(Context context, ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map, TextServicesSettings settings) { @@ -223,7 +259,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID + " calling userId = " + userId + ", foreground user id = " - + mSettings.getCurrentUserId()); + + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()); try { final String[] packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); for (int i = 0; i < packageNames.length; ++i) { @@ -237,10 +273,40 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { return true; - } else { - Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace()); - return false; } + + // Permits current profile to use TSFM as long as the current text service is the system's + // one. This is a tentative solution and should be replaced with fully functional multiuser + // support. + // TODO: Implement multiuser support in TSMS. + final boolean isCurrentProfile = mSettings.isCurrentProfile(userId); + if (DBG) { + Slog.d(TAG, "--- userId = "+ userId + " isCurrentProfile = " + isCurrentProfile); + } + if (mSettings.isCurrentProfile(userId)) { + final SpellCheckerInfo spellCheckerInfo = getCurrentSpellCheckerWithoutVerification(); + if (spellCheckerInfo != null) { + final ServiceInfo serviceInfo = spellCheckerInfo.getServiceInfo(); + final boolean isSystemSpellChecker = + (serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + if (DBG) { + Slog.d(TAG, "--- current spell checker = "+ spellCheckerInfo.getPackageName() + + " isSystem = " + isSystemSpellChecker); + } + if (isSystemSpellChecker) { + return true; + } + } + } + + // Unlike InputMethodManagerService#calledFromValidUser, INTERACT_ACROSS_USERS_FULL isn't + // taken into account here. Anyway this method is supposed to be removed once multiuser + // support is implemented. + if (DBG) { + Slog.d(TAG, "--- IPC from userId:" + userId + " is being ignored. \n" + + getStackTrace()); + } + return false; } private boolean bindCurrentSpellCheckerService( @@ -292,6 +358,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { if (!calledFromValidUser()) { return null; } + return getCurrentSpellCheckerWithoutVerification(); + } + + private SpellCheckerInfo getCurrentSpellCheckerWithoutVerification() { synchronized (mSpellCheckerMap) { final String curSpellCheckerId = mSettings.getSelectedSpellChecker(); if (DBG) { @@ -914,6 +984,10 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { private static class TextServicesSettings { private final ContentResolver mResolver; private int mCurrentUserId; + @GuardedBy("mLock") + private int[] mCurrentProfileIds = new int[0]; + private Object mLock = new Object(); + public TextServicesSettings(ContentResolver resolver, int userId) { mResolver = resolver; mCurrentUserId = userId; @@ -928,6 +1002,22 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { mCurrentUserId = userId; } + public void setCurrentProfileIds(int[] currentProfileIds) { + synchronized (mLock) { + mCurrentProfileIds = currentProfileIds; + } + } + + public boolean isCurrentProfile(int userId) { + synchronized (mLock) { + if (userId == mCurrentUserId) return true; + for (int i = 0; i < mCurrentProfileIds.length; i++) { + if (userId == mCurrentProfileIds[i]) return true; + } + return false; + } + } + public int getCurrentUserId() { return mCurrentUserId; } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 89e3f49..8e46c4d 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -415,15 +415,9 @@ public class Watchdog extends Thread { dumpKernelStackTraces(); } - // Trigger the kernel to dump all blocked threads to the kernel log - try { - FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger"); - sysrq_trigger.write("w"); - sysrq_trigger.close(); - } catch (IOException e) { - Slog.e(TAG, "Failed to write to /proc/sysrq-trigger"); - Slog.e(TAG, e.getMessage()); - } + // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log + doSysRq('w'); + doSysRq('l'); // Try to add the error to the dropbox, but assuming that the ActivityManager // itself may be deadlocked. (which has happened, causing this statement to @@ -488,6 +482,16 @@ public class Watchdog extends Thread { } } + private void doSysRq(char c) { + try { + FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger"); + sysrq_trigger.write(c); + sysrq_trigger.close(); + } catch (IOException e) { + Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e); + } + } + private File dumpKernelStackTraces() { String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null); if (tracesPath == null || tracesPath.length() == 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8dfb321..9179cc4 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1606,192 +1606,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<ProcessMemInfo> memInfos = (ArrayList<ProcessMemInfo>)msg.obj; Thread thread = new Thread() { @Override public void run() { - final SparseArray<ProcessMemInfo> infoMap - = new SparseArray<ProcessMemInfo>(memInfos.size()); - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - infoMap.put(mi.pid, mi); - } - updateCpuStatsNow(); - synchronized (mProcessCpuTracker) { - final int N = mProcessCpuTracker.countStats(); - for (int i=0; i<N; i++) { - ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); - if (st.vsize > 0) { - long pss = Debug.getPss(st.pid, null); - if (pss > 0) { - if (infoMap.indexOfKey(st.pid) < 0) { - ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, - ProcessList.NATIVE_ADJ, -1, "native", null); - mi.pss = pss; - memInfos.add(mi); - } - } - } - } - } - - long totalPss = 0; - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - if (mi.pss == 0) { - mi.pss = Debug.getPss(mi.pid, null); - } - totalPss += mi.pss; - } - Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { - @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { - if (lhs.oomAdj != rhs.oomAdj) { - return lhs.oomAdj < rhs.oomAdj ? -1 : 1; - } - if (lhs.pss != rhs.pss) { - return lhs.pss < rhs.pss ? 1 : -1; - } - return 0; - } - }); - - StringBuilder tag = new StringBuilder(128); - StringBuilder stack = new StringBuilder(128); - tag.append("Low on memory -- "); - appendMemBucket(tag, totalPss, "total", false); - appendMemBucket(stack, totalPss, "total", true); - - StringBuilder logBuilder = new StringBuilder(1024); - logBuilder.append("Low on memory:\n"); - - boolean firstLine = true; - int lastOomAdj = Integer.MIN_VALUE; - for (int i=0, N=memInfos.size(); i<N; i++) { - ProcessMemInfo mi = memInfos.get(i); - - if (mi.oomAdj != ProcessList.NATIVE_ADJ - && (mi.oomAdj < ProcessList.SERVICE_ADJ - || mi.oomAdj == ProcessList.HOME_APP_ADJ - || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { - if (lastOomAdj != mi.oomAdj) { - lastOomAdj = mi.oomAdj; - if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { - tag.append(" / "); - } - if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { - if (firstLine) { - stack.append(":"); - firstLine = false; - } - stack.append("\n\t at "); - } else { - stack.append("$"); - } - } else { - tag.append(" "); - stack.append("$"); - } - if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { - appendMemBucket(tag, mi.pss, mi.name, false); - } - appendMemBucket(stack, mi.pss, mi.name, true); - if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ - && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) { - stack.append("("); - for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { - if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { - stack.append(DUMP_MEM_OOM_LABEL[k]); - stack.append(":"); - stack.append(DUMP_MEM_OOM_ADJ[k]); - } - } - stack.append(")"); - } - } - - logBuilder.append(" "); - logBuilder.append(ProcessList.makeOomAdjString(mi.oomAdj)); - logBuilder.append(' '); - logBuilder.append(ProcessList.makeProcStateString(mi.procState)); - logBuilder.append(' '); - ProcessList.appendRamKb(logBuilder, mi.pss); - logBuilder.append(" kB: "); - logBuilder.append(mi.name); - logBuilder.append(" ("); - logBuilder.append(mi.pid); - logBuilder.append(") "); - logBuilder.append(mi.adjType); - logBuilder.append('\n'); - if (mi.adjReason != null) { - logBuilder.append(" "); - logBuilder.append(mi.adjReason); - logBuilder.append('\n'); - } - } - - logBuilder.append(" "); - ProcessList.appendRamKb(logBuilder, totalPss); - logBuilder.append(" kB: TOTAL\n"); - - long[] infos = new long[Debug.MEMINFO_COUNT]; - Debug.getMemInfo(infos); - logBuilder.append(" MemInfo: "); - logBuilder.append(infos[Debug.MEMINFO_SLAB]).append(" kB slab, "); - logBuilder.append(infos[Debug.MEMINFO_SHMEM]).append(" kB shmem, "); - logBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, "); - logBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, "); - logBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n"); - if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { - logBuilder.append(" ZRAM: "); - logBuilder.append(infos[Debug.MEMINFO_ZRAM_TOTAL]); - logBuilder.append(" kB RAM, "); - logBuilder.append(infos[Debug.MEMINFO_SWAP_TOTAL]); - logBuilder.append(" kB swap total, "); - logBuilder.append(infos[Debug.MEMINFO_SWAP_FREE]); - logBuilder.append(" kB swap free\n"); - } - Slog.i(TAG, logBuilder.toString()); - - StringBuilder dropBuilder = new StringBuilder(1024); - /* - StringWriter oomSw = new StringWriter(); - PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); - StringWriter catSw = new StringWriter(); - PrintWriter catPw = new FastPrintWriter(catSw, false, 256); - String[] emptyArgs = new String[] { }; - dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); - oomPw.flush(); - String oomString = oomSw.toString(); - */ - dropBuilder.append(stack); - dropBuilder.append('\n'); - dropBuilder.append('\n'); - dropBuilder.append(logBuilder); - dropBuilder.append('\n'); - /* - dropBuilder.append(oomString); - dropBuilder.append('\n'); - */ - StringWriter catSw = new StringWriter(); - synchronized (ActivityManagerService.this) { - PrintWriter catPw = new FastPrintWriter(catSw, false, 256); - String[] emptyArgs = new String[] { }; - catPw.println(); - dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); - catPw.println(); - mServices.dumpServicesLocked(null, catPw, emptyArgs, 0, - false, false, null); - catPw.println(); - dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); - catPw.flush(); - } - dropBuilder.append(catSw.toString()); - addErrorToDropBox("lowmem", null, "system_server", null, - null, tag.toString(), dropBuilder.toString(), null, null); - //Slog.i(TAG, "Sent to dropbox:"); - //Slog.i(TAG, dropBuilder.toString()); - synchronized (ActivityManagerService.this) { - long now = SystemClock.uptimeMillis(); - if (mLastMemUsageReportTime < now) { - mLastMemUsageReportTime = now; - } - } + reportMemUsage(memInfos); } }; thread.start(); @@ -1952,9 +1767,7 @@ public final class ActivityManagerService extends ActivityManagerNative + (SystemClock.uptimeMillis()-start) + "ms"); mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(), memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(), - memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb() - +memInfo.getSlabSizeKb(), - nativeTotalPss); + memInfo.getKernelUsedSizeKb(), nativeTotalPss); } } @@ -8849,7 +8662,8 @@ public final class ActivityManagerService extends ActivityManagerNative task = mStackSupervisor.anyTaskForIdLocked(task.taskId); if (task != null) { if (!isSystemInitiated - && ((mFocusedActivity == null) || (task != mFocusedActivity.task))) { + && ((mStackSupervisor.getFocusedStack() == null) + || (task != mStackSupervisor.getFocusedStack().topTask()))) { throw new IllegalArgumentException("Invalid task, not in foreground"); } mStackSupervisor.setLockTaskModeLocked(task, !isSystemInitiated); @@ -13923,6 +13737,35 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private static final int KSM_SHARED = 0; + private static final int KSM_SHARING = 1; + private static final int KSM_UNSHARED = 2; + private static final int KSM_VOLATILE = 3; + + private final long[] getKsmInfo() { + long[] longOut = new long[4]; + final int[] SINGLE_LONG_FORMAT = new int[] { + Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG + }; + long[] longTmp = new long[1]; + Process.readProcFile("/sys/kernel/mm/ksm/pages_shared", + SINGLE_LONG_FORMAT, null, longTmp, null); + longOut[KSM_SHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; + longTmp[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing", + SINGLE_LONG_FORMAT, null, longTmp, null); + longOut[KSM_SHARING] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; + longTmp[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared", + SINGLE_LONG_FORMAT, null, longTmp, null); + longOut[KSM_UNSHARED] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; + longTmp[0] = 0; + Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", + SINGLE_LONG_FORMAT, null, longTmp, null); + longOut[KSM_VOLATILE] = longTmp[0] * ProcessList.PAGE_SIZE / 1024; + return longOut; + } + final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief, PrintWriter categoryPw) { boolean dumpDetails = false; @@ -14240,8 +14083,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(), memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(), - memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()+memInfo.getSlabSizeKb(), - nativeProcTotalPss); + memInfo.getKernelUsedSizeKb(), nativeProcTotalPss); } } if (!brief) { @@ -14269,7 +14111,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.print(" Free RAM: "); pw.print(cachedPss + memInfo.getCachedSizeKb() + memInfo.getFreeSizeKb()); pw.print(" kB ("); pw.print(cachedPss); pw.print(" cached pss + "); - pw.print(memInfo.getCachedSizeKb()); pw.print(" cached + "); + pw.print(memInfo.getCachedSizeKb()); pw.print(" cached kernel + "); pw.print(memInfo.getFreeSizeKb()); pw.println(" free)"); } else { pw.print("ram,"); pw.print(memInfo.getTotalSizeKb()); pw.print(","); @@ -14280,16 +14122,12 @@ public final class ActivityManagerService extends ActivityManagerNative } if (!isCompact) { pw.print(" Used RAM: "); pw.print(totalPss - cachedPss - + memInfo.getBuffersSizeKb() + memInfo.getShmemSizeKb() - + memInfo.getSlabSizeKb()); pw.print(" kB ("); + + memInfo.getKernelUsedSizeKb()); pw.print(" kB ("); pw.print(totalPss - cachedPss); pw.print(" used pss + "); - pw.print(memInfo.getBuffersSizeKb()); pw.print(" buffers + "); - pw.print(memInfo.getShmemSizeKb()); pw.print(" shmem + "); - pw.print(memInfo.getSlabSizeKb()); pw.println(" slab)"); + pw.print(memInfo.getKernelUsedSizeKb()); pw.print(" kernel)\n"); pw.print(" Lost RAM: "); pw.print(memInfo.getTotalSizeKb() - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() - - memInfo.getBuffersSizeKb() - memInfo.getShmemSizeKb() - - memInfo.getSlabSizeKb()); pw.println(" kB"); + - memInfo.getKernelUsedSizeKb()); pw.println(" kB"); } if (!brief) { if (memInfo.getZramTotalSizeKb() != 0) { @@ -14307,32 +14145,16 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(memInfo.getSwapFreeSizeKb()); } } - final int[] SINGLE_LONG_FORMAT = new int[] { - Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG - }; - long[] longOut = new long[1]; - Process.readProcFile("/sys/kernel/mm/ksm/pages_shared", - SINGLE_LONG_FORMAT, null, longOut, null); - long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing", - SINGLE_LONG_FORMAT, null, longOut, null); - long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared", - SINGLE_LONG_FORMAT, null, longOut, null); - long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024; - longOut[0] = 0; - Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", - SINGLE_LONG_FORMAT, null, longOut, null); - long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024; + final long[] ksm = getKsmInfo(); if (!isCompact) { - if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) { - pw.print(" KSM: "); pw.print(sharing); + if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 + || ksm[KSM_VOLATILE] != 0) { + pw.print(" KSM: "); pw.print(ksm[KSM_SHARING]); pw.print(" kB saved from shared "); - pw.print(shared); pw.println(" kB"); - pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); - pw.print(voltile); pw.println(" kB volatile"); + pw.print(ksm[KSM_SHARED]); pw.println(" kB"); + pw.print(" "); pw.print(ksm[KSM_UNSHARED]); + pw.print(" kB unshared; "); + pw.print(ksm[KSM_VOLATILE]); pw.println(" kB volatile"); } pw.print(" Tuning: "); pw.print(ActivityManager.staticGetMemoryClass()); @@ -14352,9 +14174,9 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(); } else { - pw.print("ksm,"); pw.print(sharing); pw.print(","); - pw.print(shared); pw.print(","); pw.print(unshared); pw.print(","); - pw.println(voltile); + pw.print("ksm,"); pw.print(ksm[KSM_SHARING]); pw.print(","); + pw.print(ksm[KSM_SHARED]); pw.print(","); pw.print(ksm[KSM_UNSHARED]); + pw.print(","); pw.println(ksm[KSM_VOLATILE]); pw.print("tuning,"); pw.print(ActivityManager.staticGetMemoryClass()); pw.print(','); @@ -14373,6 +14195,265 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void appendBasicMemEntry(StringBuilder sb, int oomAdj, int procState, long pss, + String name) { + sb.append(" "); + sb.append(ProcessList.makeOomAdjString(oomAdj)); + sb.append(' '); + sb.append(ProcessList.makeProcStateString(procState)); + sb.append(' '); + ProcessList.appendRamKb(sb, pss); + sb.append(" kB: "); + sb.append(name); + } + + private void appendMemInfo(StringBuilder sb, ProcessMemInfo mi) { + appendBasicMemEntry(sb, mi.oomAdj, mi.procState, mi.pss, mi.name); + sb.append(" ("); + sb.append(mi.pid); + sb.append(") "); + sb.append(mi.adjType); + sb.append('\n'); + if (mi.adjReason != null) { + sb.append(" "); + sb.append(mi.adjReason); + sb.append('\n'); + } + } + + void reportMemUsage(ArrayList<ProcessMemInfo> memInfos) { + final SparseArray<ProcessMemInfo> infoMap = new SparseArray<>(memInfos.size()); + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + infoMap.put(mi.pid, mi); + } + updateCpuStatsNow(); + synchronized (mProcessCpuTracker) { + final int N = mProcessCpuTracker.countStats(); + for (int i=0; i<N; i++) { + ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i); + if (st.vsize > 0) { + long pss = Debug.getPss(st.pid, null); + if (pss > 0) { + if (infoMap.indexOfKey(st.pid) < 0) { + ProcessMemInfo mi = new ProcessMemInfo(st.name, st.pid, + ProcessList.NATIVE_ADJ, -1, "native", null); + mi.pss = pss; + memInfos.add(mi); + } + } + } + } + } + + long totalPss = 0; + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + if (mi.pss == 0) { + mi.pss = Debug.getPss(mi.pid, null); + } + totalPss += mi.pss; + } + Collections.sort(memInfos, new Comparator<ProcessMemInfo>() { + @Override public int compare(ProcessMemInfo lhs, ProcessMemInfo rhs) { + if (lhs.oomAdj != rhs.oomAdj) { + return lhs.oomAdj < rhs.oomAdj ? -1 : 1; + } + if (lhs.pss != rhs.pss) { + return lhs.pss < rhs.pss ? 1 : -1; + } + return 0; + } + }); + + StringBuilder tag = new StringBuilder(128); + StringBuilder stack = new StringBuilder(128); + tag.append("Low on memory -- "); + appendMemBucket(tag, totalPss, "total", false); + appendMemBucket(stack, totalPss, "total", true); + + StringBuilder fullNativeBuilder = new StringBuilder(1024); + StringBuilder shortNativeBuilder = new StringBuilder(1024); + StringBuilder fullJavaBuilder = new StringBuilder(1024); + + boolean firstLine = true; + int lastOomAdj = Integer.MIN_VALUE; + long extraNativeRam = 0; + long cachedPss = 0; + for (int i=0, N=memInfos.size(); i<N; i++) { + ProcessMemInfo mi = memInfos.get(i); + + if (mi.oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + cachedPss += mi.pss; + } + + if (mi.oomAdj != ProcessList.NATIVE_ADJ + && (mi.oomAdj < ProcessList.SERVICE_ADJ + || mi.oomAdj == ProcessList.HOME_APP_ADJ + || mi.oomAdj == ProcessList.PREVIOUS_APP_ADJ)) { + if (lastOomAdj != mi.oomAdj) { + lastOomAdj = mi.oomAdj; + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + tag.append(" / "); + } + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ) { + if (firstLine) { + stack.append(":"); + firstLine = false; + } + stack.append("\n\t at "); + } else { + stack.append("$"); + } + } else { + tag.append(" "); + stack.append("$"); + } + if (mi.oomAdj <= ProcessList.FOREGROUND_APP_ADJ) { + appendMemBucket(tag, mi.pss, mi.name, false); + } + appendMemBucket(stack, mi.pss, mi.name, true); + if (mi.oomAdj >= ProcessList.FOREGROUND_APP_ADJ + && ((i+1) >= N || memInfos.get(i+1).oomAdj != lastOomAdj)) { + stack.append("("); + for (int k=0; k<DUMP_MEM_OOM_ADJ.length; k++) { + if (DUMP_MEM_OOM_ADJ[k] == mi.oomAdj) { + stack.append(DUMP_MEM_OOM_LABEL[k]); + stack.append(":"); + stack.append(DUMP_MEM_OOM_ADJ[k]); + } + } + stack.append(")"); + } + } + + appendMemInfo(fullNativeBuilder, mi); + if (mi.oomAdj == ProcessList.NATIVE_ADJ) { + // The short form only has native processes that are >= 1MB. + if (mi.pss >= 1000) { + appendMemInfo(shortNativeBuilder, mi); + } else { + extraNativeRam += mi.pss; + } + } else { + // Short form has all other details, but if we have collected RAM + // from smaller native processes let's dump a summary of that. + if (extraNativeRam > 0) { + appendBasicMemEntry(shortNativeBuilder, ProcessList.NATIVE_ADJ, + -1, extraNativeRam, "(Other native)"); + shortNativeBuilder.append('\n'); + extraNativeRam = 0; + } + appendMemInfo(fullJavaBuilder, mi); + } + } + + fullJavaBuilder.append(" "); + ProcessList.appendRamKb(fullJavaBuilder, totalPss); + fullJavaBuilder.append(" kB: TOTAL\n"); + + MemInfoReader memInfo = new MemInfoReader(); + memInfo.readMemInfo(); + final long[] infos = memInfo.getRawInfo(); + + StringBuilder memInfoBuilder = new StringBuilder(1024); + Debug.getMemInfo(infos); + memInfoBuilder.append(" MemInfo: "); + memInfoBuilder.append(infos[Debug.MEMINFO_SLAB]).append(" kB slab, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SHMEM]).append(" kB shmem, "); + memInfoBuilder.append(infos[Debug.MEMINFO_VM_ALLOC_USED]).append(" kB vm alloc, "); + memInfoBuilder.append(infos[Debug.MEMINFO_PAGE_TABLES]).append(" kB page tables "); + memInfoBuilder.append(infos[Debug.MEMINFO_KERNEL_STACK]).append(" kB kernel stack\n"); + memInfoBuilder.append(" "); + memInfoBuilder.append(infos[Debug.MEMINFO_BUFFERS]).append(" kB buffers, "); + memInfoBuilder.append(infos[Debug.MEMINFO_CACHED]).append(" kB cached, "); + memInfoBuilder.append(infos[Debug.MEMINFO_MAPPED]).append(" kB mapped, "); + memInfoBuilder.append(infos[Debug.MEMINFO_FREE]).append(" kB free\n"); + if (infos[Debug.MEMINFO_ZRAM_TOTAL] != 0) { + memInfoBuilder.append(" ZRAM: "); + memInfoBuilder.append(infos[Debug.MEMINFO_ZRAM_TOTAL]); + memInfoBuilder.append(" kB RAM, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_TOTAL]); + memInfoBuilder.append(" kB swap total, "); + memInfoBuilder.append(infos[Debug.MEMINFO_SWAP_FREE]); + memInfoBuilder.append(" kB swap free\n"); + } + final long[] ksm = getKsmInfo(); + if (ksm[KSM_SHARING] != 0 || ksm[KSM_SHARED] != 0 || ksm[KSM_UNSHARED] != 0 + || ksm[KSM_VOLATILE] != 0) { + memInfoBuilder.append(" KSM: "); memInfoBuilder.append(ksm[KSM_SHARING]); + memInfoBuilder.append(" kB saved from shared "); + memInfoBuilder.append(ksm[KSM_SHARED]); memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" "); memInfoBuilder.append(ksm[KSM_UNSHARED]); + memInfoBuilder.append(" kB unshared; "); + memInfoBuilder.append(ksm[KSM_VOLATILE]); memInfoBuilder.append(" kB volatile\n"); + } + memInfoBuilder.append(" Free RAM: "); + memInfoBuilder.append(cachedPss + memInfo.getCachedSizeKb() + + memInfo.getFreeSizeKb()); + memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" Used RAM: "); + memInfoBuilder.append(totalPss - cachedPss + memInfo.getKernelUsedSizeKb()); + memInfoBuilder.append(" kB\n"); + memInfoBuilder.append(" Lost RAM: "); + memInfoBuilder.append(memInfo.getTotalSizeKb() + - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb() + - memInfo.getKernelUsedSizeKb()); + memInfoBuilder.append(" kB\n"); + Slog.i(TAG, "Low on memory:"); + Slog.i(TAG, shortNativeBuilder.toString()); + Slog.i(TAG, fullJavaBuilder.toString()); + Slog.i(TAG, memInfoBuilder.toString()); + + StringBuilder dropBuilder = new StringBuilder(1024); + /* + StringWriter oomSw = new StringWriter(); + PrintWriter oomPw = new FastPrintWriter(oomSw, false, 256); + StringWriter catSw = new StringWriter(); + PrintWriter catPw = new FastPrintWriter(catSw, false, 256); + String[] emptyArgs = new String[] { }; + dumpApplicationMemoryUsage(null, oomPw, " ", emptyArgs, true, catPw); + oomPw.flush(); + String oomString = oomSw.toString(); + */ + dropBuilder.append("Low on memory:"); + dropBuilder.append(stack); + dropBuilder.append('\n'); + dropBuilder.append(fullNativeBuilder); + dropBuilder.append(fullJavaBuilder); + dropBuilder.append('\n'); + dropBuilder.append(memInfoBuilder); + dropBuilder.append('\n'); + /* + dropBuilder.append(oomString); + dropBuilder.append('\n'); + */ + StringWriter catSw = new StringWriter(); + synchronized (ActivityManagerService.this) { + PrintWriter catPw = new FastPrintWriter(catSw, false, 256); + String[] emptyArgs = new String[] { }; + catPw.println(); + dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); + catPw.println(); + mServices.dumpServicesLocked(null, catPw, emptyArgs, 0, + false, false, null); + catPw.println(); + dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.flush(); + } + dropBuilder.append(catSw.toString()); + addErrorToDropBox("lowmem", null, "system_server", null, + null, tag.toString(), dropBuilder.toString(), null, null); + //Slog.i(TAG, "Sent to dropbox:"); + //Slog.i(TAG, dropBuilder.toString()); + synchronized (ActivityManagerService.this) { + long now = SystemClock.uptimeMillis(); + if (mLastMemUsageReportTime < now) { + mLastMemUsageReportTime = now; + } + } + } + /** * Searches array of arguments for the specified string * @param args array of argument strings diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 03dd3c0..eaff6be 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -120,7 +120,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_RELEASE = DEBUG || false; static final boolean DEBUG_SAVED_STATE = DEBUG || false; static final boolean DEBUG_SCREENSHOTS = DEBUG || false; - static final boolean DEBUG_STATES = DEBUG || false; + static final boolean DEBUG_STATES = DEBUG || true; static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -444,10 +444,6 @@ public final class ActivityStackSupervisor implements DisplayListener { return mService.startHomeActivityLocked(mCurrentUser); } - void keyguardWaitingForActivityDrawn() { - mWindowManager.keyguardWaitingForActivityDrawn(); - } - TaskRecord anyTaskForIdLocked(int id) { int numDisplays = mActivityDisplays.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java index 5768ddb..b3777ed 100644 --- a/services/core/java/com/android/server/am/LockTaskNotify.java +++ b/services/core/java/com/android/server/am/LockTaskNotify.java @@ -19,6 +19,7 @@ package com.android.server.am; import android.content.Context; import android.os.Handler; import android.os.Message; +import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; @@ -56,8 +57,7 @@ public class LockTaskNotify { if (mLastToast != null) { mLastToast.cancel(); } - mLastToast = Toast.makeText(mContext, text, Toast.LENGTH_LONG); - mLastToast.show(); + mLastToast = makeAllUserToastAndShow(text); } public void show(boolean starting) { @@ -65,7 +65,15 @@ public class LockTaskNotify { if (starting) { showString = R.string.lock_to_app_start; } - Toast.makeText(mContext, mContext.getString(showString), Toast.LENGTH_LONG).show(); + makeAllUserToastAndShow(mContext.getString(showString)); + } + + private Toast makeAllUserToastAndShow(String text) { + Toast toast = Toast.makeText(mContext, text, Toast.LENGTH_LONG); + toast.getWindowParams().privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + toast.show(); + return toast; } private final class H extends Handler { diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index afc781f..b331c84 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -166,7 +166,7 @@ public class TaskPersister { break; } } - if (queueNdx < 0) { + if (queueNdx < 0 && task.isPersistable) { mWriteQueue.add(new TaskWriteQueueItem(task)); } } else { @@ -473,13 +473,15 @@ public class TaskPersister { if (DEBUG) Slog.d(TAG, "mRecents=" + tasks); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = tasks.get(taskNdx); - if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" + - task.isPersistable); - if (task.isPersistable && !task.stack.isHomeStack()) { + if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + + " persistable=" + task.isPersistable); + if ((task.isPersistable || task.inRecents) + && !task.stack.isHomeStack()) { if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task); persistentTaskIds.add(task.taskId); } else { - if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task); + if (DEBUG) Slog.d(TAG, + "omitting from persistentTaskIds task=" + task); } } } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 4dfd23b..ee93233 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -144,7 +144,7 @@ final class TaskRecord { boolean mReuseTask = false; private Bitmap mLastThumbnail; // Last thumbnail captured for this item. - private final File mLastThumbnailFile; // File containing last thubmnail. + private final File mLastThumbnailFile; // File containing last thumbnail. private final String mFilename; CharSequence lastDescription; // Last description captured for this item. diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index dfc8df5..36263ec 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -17,17 +17,11 @@ package com.android.server.am; import android.app.AlertDialog; -import android.app.Service; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.res.Resources; -import android.os.Handler; -import android.os.Message; -import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.TextView; @@ -39,11 +33,10 @@ import com.android.internal.R; * in the background rather than just freeze the screen and not know if the user-switch affordance * was being handled. */ -final class UserSwitchingDialog extends AlertDialog { +final class UserSwitchingDialog extends AlertDialog + implements ViewTreeObserver.OnWindowShownListener { private static final String TAG = "ActivityManagerUserSwitchingDialog"; - private static final int MSG_START_USER = 1; - private final ActivityManagerService mService; private final int mUserId; @@ -74,19 +67,21 @@ final class UserSwitchingDialog extends AlertDialog { @Override public void show() { + // Slog.v(TAG, "show called"); super.show(); - // TODO: Instead of just an arbitrary delay, wait for a signal that the window was fully - // displayed by the window manager - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_USER), 250); + final View decorView = getWindow().getDecorView(); + if (decorView != null) { + decorView.getViewTreeObserver().addOnWindowShownListener(this); + } } - private final Handler mHandler = new Handler() { - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START_USER: - mService.startUserInForeground(mUserId, UserSwitchingDialog.this); - break; - } + @Override + public void onWindowShown() { + // Slog.v(TAG, "onWindowShown called"); + mService.startUserInForeground(mUserId, this); + final View decorView = getWindow().getDecorView(); + if (decorView != null) { + decorView.getViewTreeObserver().removeOnWindowShownListener(this); } - }; + } } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index c382be0..576556b 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; import java.net.Inet4Address; @@ -43,45 +44,41 @@ import com.android.server.net.BaseNetworkObserver; * Class to manage a 464xlat CLAT daemon. */ public class Nat464Xlat extends BaseNetworkObserver { - private Context mContext; - private INetworkManagementService mNMService; - private IConnectivityManager mConnService; - // Whether we started clatd and expect it to be running. - private boolean mIsStarted; - // Whether the clatd interface exists (i.e., clatd is running). - private boolean mIsRunning; - // The LinkProperties of the clat interface. - private LinkProperties mLP; - // Current LinkProperties of the network. Includes mLP as a stacked link when clat is active. - private LinkProperties mBaseLP; + private static final String TAG = "Nat464Xlat"; + + // This must match the interface prefix in clatd.c. + private static final String CLAT_PREFIX = "v4-"; + + private final INetworkManagementService mNMService; + // ConnectivityService Handler for LinkProperties updates. - private Handler mHandler; - // Marker to connote which network we're augmenting. - private Messenger mNetworkMessenger; + private final Handler mHandler; - // This must match the interface name in clatd.conf. - private static final String CLAT_INTERFACE_NAME = "clat4"; + // The network we're running on, and its type. + private final NetworkAgentInfo mNetwork; - private static final String TAG = "Nat464Xlat"; + // Internal state variables. + // + // The possible states are: + // - Idle: start() not called. Everything is null. + // - Starting: start() called. Interfaces are non-null. isStarted() returns true. + // mIsRunning is false. + // - Running: start() called, and interfaceAdded() told us that mIface is up. Clat IP address + // is non-null. mIsRunning is true. + // + // Once mIface is non-null and isStarted() is true, methods called by ConnectivityService on + // its handler thread must not modify any internal state variables; they are only updated by the + // interface observers, called on the notification threads. + private String mBaseIface; + private String mIface; + private boolean mIsRunning; - public Nat464Xlat(Context context, INetworkManagementService nmService, - IConnectivityManager connService, Handler handler) { - mContext = context; + public Nat464Xlat( + Context context, INetworkManagementService nmService, + Handler handler, NetworkAgentInfo nai) { mNMService = nmService; - mConnService = connService; mHandler = handler; - - mIsStarted = false; - mIsRunning = false; - mLP = new LinkProperties(); - - // If this is a runtime restart, it's possible that clatd is already - // running, but we don't know about it. If so, stop it. - try { - if (mNMService.isClatdStarted()) { - mNMService.stopClatd(); - } - } catch(RemoteException e) {} // Well, we tried. + mNetwork = nai; } /** @@ -94,137 +91,196 @@ public class Nat464Xlat extends BaseNetworkObserver { final boolean connected = nai.networkInfo.isConnected(); final boolean hasIPv4Address = (nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false; - Slog.d(TAG, "requiresClat: netType=" + netType + - ", connected=" + connected + - ", hasIPv4Address=" + hasIPv4Address); - // Only support clat on mobile for now. - return netType == TYPE_MOBILE && connected && !hasIPv4Address; + // Only support clat on mobile and wifi for now, because these are the only IPv6-only + // networks we can connect to. + return connected && !hasIPv4Address && (netType == TYPE_MOBILE || netType == TYPE_WIFI); } - public boolean isRunningClat(NetworkAgentInfo network) { - return mNetworkMessenger == network.messenger; + /** + * Determines whether clatd is started. Always true, except a) if start has not yet been called, + * or b) if our interface was removed. + */ + public boolean isStarted() { + return mIface != null; + } + + /** + * Clears internal state. Must not be called by ConnectivityService. + */ + private void clear() { + mIface = null; + mBaseIface = null; + mIsRunning = false; } /** - * Starts the clat daemon. - * @param lp The link properties of the interface to start clatd on. + * Starts the clat daemon. Called by ConnectivityService on the handler thread. */ - public void startClat(NetworkAgentInfo network) { - if (mNetworkMessenger != null && mNetworkMessenger != network.messenger) { - Slog.e(TAG, "startClat: too many networks requesting clat"); + public void start() { + if (isStarted()) { + Slog.e(TAG, "startClat: already started"); return; } - mNetworkMessenger = network.messenger; - LinkProperties lp = network.linkProperties; - mBaseLP = new LinkProperties(lp); - if (mIsStarted) { - Slog.e(TAG, "startClat: already started"); + + if (mNetwork.linkProperties == null) { + Slog.e(TAG, "startClat: Can't start clat with null LinkProperties"); return; } - String iface = lp.getInterfaceName(); - Slog.i(TAG, "Starting clatd on " + iface + ", lp=" + lp); + try { - mNMService.startClatd(iface); + mNMService.registerObserver(this); } catch(RemoteException e) { - Slog.e(TAG, "Error starting clat daemon: " + e); + Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork); + return; + } + + mBaseIface = mNetwork.linkProperties.getInterfaceName(); + if (mBaseIface == null) { + Slog.e(TAG, "startClat: Can't start clat on null interface"); + return; + } + mIface = CLAT_PREFIX + mBaseIface; + // From now on, isStarted() will return true. + + Slog.i(TAG, "Starting clatd on " + mBaseIface); + try { + mNMService.startClatd(mBaseIface); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error starting clatd: " + e); } - mIsStarted = true; } /** - * Stops the clat daemon. + * Stops the clat daemon. Called by ConnectivityService on the handler thread. */ - public void stopClat() { - if (mIsStarted) { + public void stop() { + if (isStarted()) { Slog.i(TAG, "Stopping clatd"); try { - mNMService.stopClatd(); - } catch(RemoteException e) { - Slog.e(TAG, "Error stopping clat daemon: " + e); + mNMService.stopClatd(mBaseIface); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error stopping clatd: " + e); } - mIsStarted = false; - mIsRunning = false; - mNetworkMessenger = null; - mBaseLP = null; - mLP.clear(); + // When clatd stops and its interface is deleted, interfaceRemoved() will notify + // ConnectivityService and call clear(). } else { - Slog.e(TAG, "stopClat: already stopped"); + Slog.e(TAG, "clatd: already stopped"); } } - private void updateConnectivityService() { - Message msg = mHandler.obtainMessage( - NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, mBaseLP); - msg.replyTo = mNetworkMessenger; + private void updateConnectivityService(LinkProperties lp) { + Message msg = mHandler.obtainMessage(NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED, lp); + msg.replyTo = mNetwork.messenger; Slog.i(TAG, "sending message to ConnectivityService: " + msg); msg.sendToTarget(); } - // Copies the stacked clat link in oldLp, if any, to the LinkProperties in nai. - public void fixupLinkProperties(NetworkAgentInfo nai, LinkProperties oldLp) { - if (isRunningClat(nai) && - nai.linkProperties != null && - !nai.linkProperties.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME)) { - Slog.d(TAG, "clatd running, updating NAI for " + nai.linkProperties.getInterfaceName()); + /** + * Copies the stacked clat link in oldLp, if any, to the LinkProperties in mNetwork. + * This is necessary because the LinkProperties in mNetwork come from the transport layer, which + * has no idea that 464xlat is running on top of it. + */ + public void fixupLinkProperties(LinkProperties oldLp) { + if (mNetwork.clatd != null && + mIsRunning && + mNetwork.linkProperties != null && + !mNetwork.linkProperties.getAllInterfaceNames().contains(mIface)) { + Slog.d(TAG, "clatd running, updating NAI for " + mIface); for (LinkProperties stacked: oldLp.getStackedLinks()) { - if (CLAT_INTERFACE_NAME.equals(stacked.getInterfaceName())) { - nai.linkProperties.addStackedLink(stacked); + if (mIface.equals(stacked.getInterfaceName())) { + mNetwork.linkProperties.addStackedLink(stacked); break; } } } } + private LinkProperties makeLinkProperties(LinkAddress clatAddress) { + LinkProperties stacked = new LinkProperties(); + stacked.setInterfaceName(mIface); + + // Although the clat interface is a point-to-point tunnel, we don't + // point the route directly at the interface because some apps don't + // understand routes without gateways (see, e.g., http://b/9597256 + // http://b/9597516). Instead, set the next hop of the route to the + // clat IPv4 address itself (for those apps, it doesn't matter what + // the IP of the gateway is, only that there is one). + RouteInfo ipv4Default = new RouteInfo( + new LinkAddress(Inet4Address.ANY, 0), + clatAddress.getAddress(), mIface); + stacked.addRoute(ipv4Default); + stacked.addLinkAddress(clatAddress); + return stacked; + } + + private LinkAddress getLinkAddress(String iface) { + try { + InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); + return config.getLinkAddress(); + } catch(RemoteException|IllegalStateException e) { + Slog.e(TAG, "Error getting link properties: " + e); + return null; + } + } + + private void maybeSetIpv6NdOffload(String iface, boolean on) { + if (mNetwork.networkInfo.getType() != TYPE_WIFI) { + return; + } + try { + Slog.d(TAG, (on ? "En" : "Dis") + "abling ND offload on " + iface); + mNMService.setInterfaceIpv6NdOffload(iface, on); + } catch(RemoteException|IllegalStateException e) { + Slog.w(TAG, "Changing IPv6 ND offload on " + iface + "failed: " + e); + } + } + @Override public void interfaceAdded(String iface) { - if (iface.equals(CLAT_INTERFACE_NAME)) { - Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME + - " added, mIsRunning = " + mIsRunning + " -> true"); - mIsRunning = true; - - // Create the LinkProperties for the clat interface by fetching the - // IPv4 address for the interface and adding an IPv4 default route, - // then stack the LinkProperties on top of the link it's running on. - try { - InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); - LinkAddress clatAddress = config.getLinkAddress(); - mLP.clear(); - mLP.setInterfaceName(iface); - - // Although the clat interface is a point-to-point tunnel, we don't - // point the route directly at the interface because some apps don't - // understand routes without gateways (see, e.g., http://b/9597256 - // http://b/9597516). Instead, set the next hop of the route to the - // clat IPv4 address itself (for those apps, it doesn't matter what - // the IP of the gateway is, only that there is one). - RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0), - clatAddress.getAddress(), iface); - mLP.addRoute(ipv4Default); - mLP.addLinkAddress(clatAddress); - mBaseLP.addStackedLink(mLP); - Slog.i(TAG, "Adding stacked link. tracker LP: " + mBaseLP); - updateConnectivityService(); - } catch(RemoteException e) { - Slog.e(TAG, "Error getting link properties: " + e); + // Called by the InterfaceObserver on its own thread, so can race with stop(). + if (isStarted() && mIface.equals(iface)) { + Slog.i(TAG, "interface " + iface + " added, mIsRunning " + mIsRunning + "->true"); + + if (!mIsRunning) { + LinkAddress clatAddress = getLinkAddress(iface); + if (clatAddress == null) { + return; + } + mIsRunning = true; + maybeSetIpv6NdOffload(mBaseIface, false); + LinkProperties lp = new LinkProperties(mNetwork.linkProperties); + lp.addStackedLink(makeLinkProperties(clatAddress)); + Slog.i(TAG, "Adding stacked link " + mIface + " on top of " + mBaseIface); + updateConnectivityService(lp); } } } @Override public void interfaceRemoved(String iface) { - if (iface == CLAT_INTERFACE_NAME) { + if (isStarted() && mIface.equals(iface)) { + Slog.i(TAG, "interface " + iface + " removed, mIsRunning " + mIsRunning + "->false"); + if (mIsRunning) { - NetworkUtils.resetConnections( - CLAT_INTERFACE_NAME, - NetworkUtils.RESET_IPV4_ADDRESSES); - mBaseLP.removeStackedLink(mLP); - updateConnectivityService(); + // The interface going away likely means clatd has crashed. Ask netd to stop it, + // because otherwise when we try to start it again on the same base interface netd + // will complain that it's already started. + // + // Note that this method can be called by the interface observer at the same time + // that ConnectivityService calls stop(). In this case, the second call to + // stopClatd() will just throw IllegalStateException, which we'll ignore. + try { + mNMService.unregisterObserver(this); + mNMService.stopClatd(mBaseIface); + } catch (RemoteException|IllegalStateException e) { + // Well, we tried. + } + maybeSetIpv6NdOffload(mBaseIface, true); + LinkProperties lp = new LinkProperties(mNetwork.linkProperties); + lp.removeStackedLink(mIface); + clear(); + updateConnectivityService(lp); } - Slog.i(TAG, "interface " + CLAT_INTERFACE_NAME + - " removed, mIsRunning = " + mIsRunning + " -> false"); - mIsRunning = false; - mLP.clear(); - Slog.i(TAG, "mLP = " + mLP); } } -}; +} diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 15ffc0d..4cf2a4a 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -63,6 +63,9 @@ public class NetworkAgentInfo { public final Messenger messenger; public final AsyncChannel asyncChannel; + // Used by ConnectivityService to keep track of 464xlat. + public Nat464Xlat clatd; + public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc) { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index 07fe7ba..7d1da01 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -265,14 +265,9 @@ public class PacManager { } Intent intent = new Intent(); intent.setClassName(PAC_PACKAGE, PAC_SERVICE); - // Already bound no need to bind again. if ((mProxyConnection != null) && (mConnection != null)) { - if (mLastPort != -1) { - sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort)); - } else { - Log.e(TAG, "Received invalid port from Local Proxy," - + " PAC will not be operational"); - } + // Already bound no need to bind again, just download the new file. + IoThread.getHandler().post(mPacDownloader); return; } mConnection = new ServiceConnection() { diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 3f6b71a..83756aa 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -215,7 +215,7 @@ public class Vpn { */ public synchronized boolean prepare(String oldPackage, String newPackage) { // Return false if the package does not match. - if (oldPackage != null && !oldPackage.equals(mPackage)) { + if (oldPackage != null && getAppUid(oldPackage, mUserHandle) != mOwnerUID) { // The package doesn't match. If this VPN was not previously authorized, return false // to force user authorization. Otherwise, revoke the VPN anyway. if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) { @@ -234,8 +234,8 @@ public class Vpn { } // Return true if we do not need to revoke. - if (newPackage == null || - (newPackage.equals(mPackage) && !newPackage.equals(VpnConfig.LEGACY_VPN))) { + if (newPackage == null || (!newPackage.equals(VpnConfig.LEGACY_VPN) && + getAppUid(newPackage, mUserHandle) == mOwnerUID)) { return true; } diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 3b23b6a..f514531 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -248,6 +248,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mInfo.width = mWidth; mInfo.height = mHeight; mInfo.refreshRate = mRefreshRate; + mInfo.supportedRefreshRates = new float[] { mRefreshRate }; mInfo.densityDpi = mDensityDpi; mInfo.xDpi = mDensityDpi; mInfo.yDpi = mDensityDpi; diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index c2b4478..0232bad 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -257,6 +257,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { mInfo.width = mWidth; mInfo.height = mHeight; mInfo.refreshRate = 60; + mInfo.supportedRefreshRates = new float[] { 60.0f }; mInfo.densityDpi = mDensityDpi; mInfo.xDpi = mDensityDpi; mInfo.yDpi = mDensityDpi; diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index a17d731..6b010d9 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -625,6 +625,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { mInfo.width = mWidth; mInfo.height = mHeight; mInfo.refreshRate = mRefreshRate; + mInfo.supportedRefreshRates = new float[] { mRefreshRate }; mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame mInfo.flags = mFlags; mInfo.type = Display.TYPE_WIFI; diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java index cb92112..9593a9c 100644 --- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java +++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java @@ -57,8 +57,9 @@ final class ActiveSourceHandler { * Handles the incoming active source command. * * @param newActive new active source information + * @param deviceType device type of the new active source */ - void process(ActiveSource newActive) { + void process(ActiveSource newActive, int deviceType) { // Seq #17 HdmiCecLocalDeviceTv tv = mSource; ActiveSource activeSource = tv.getActiveSource(); @@ -68,7 +69,7 @@ final class ActiveSourceHandler { } HdmiDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress); if (device == null) { - tv.startNewDeviceAction(newActive); + tv.startNewDeviceAction(newActive, deviceType); } if (!tv.isProhibitMode()) { diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index b0a3a66..bb12eae 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -205,13 +205,6 @@ final class Constants { static final int UNKNOWN_VOLUME = -1; - // IRT(Initiator Repetition Time) in millisecond as recommended in the standard. - // Outgoing UCP commands, when in 'Press and Hold' mode, should be this much apart - // from the adjacent one so as not to place unnecessarily heavy load on the CEC line. - // TODO: This value might need tweaking per product basis. Consider putting it - // in config.xml to allow customization. - static final int IRT_MS = 300; - static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback"; static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv"; diff --git a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java index d965caa..3dd1522 100644 --- a/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java +++ b/services/core/java/com/android/server/hdmi/DevicePowerStatusAction.java @@ -70,7 +70,8 @@ final class DevicePowerStatusAction extends HdmiCecFeatureAction { @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAITING_FOR_REPORT_POWER_STATUS) { + if (mState != STATE_WAITING_FOR_REPORT_POWER_STATUS + || mTargetAddress != cmd.getSource()) { return false; } if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java index d155e84..5a1d896 100644 --- a/services/core/java/com/android/server/hdmi/DeviceSelectAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceSelectAction.java @@ -95,7 +95,7 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { sendCommand(mGivePowerStatus, new SendMessageCallback() { @Override public void onSendCompleted(int error) { - if (error == Constants.SEND_RESULT_NAK) { + if (error != Constants.SEND_RESULT_SUCCESS) { invokeCallback(HdmiControlManager.RESULT_COMMUNICATION_FAILED); finish(); return; @@ -168,6 +168,10 @@ final class DeviceSelectAction extends HdmiCecFeatureAction { } private void sendSetStreamPath() { + // Turn the active source invalidated, which remains so till <Active Source> comes from + // the selected device. + tv().getActiveSource().invalidate(); + tv().setActivePath(mTarget.getPhysicalAddress()); sendCommand(HdmiCecMessageBuilder.buildSetStreamPath( getSourceAddress(), mTarget.getPhysicalAddress())); invokeCallback(HdmiControlManager.RESULT_SUCCESS); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java index fc53c50..d26be57 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java @@ -85,8 +85,7 @@ abstract class HdmiCecFeatureAction { * Process the command. Called whenever a new command arrives. * * @param cmd command to process - * @return true if the command was consumed in the process; Otherwise false, which - * indicates that the command shall be handled by other actions. + * @return true if the command was consumed in the process; Otherwise false. */ abstract boolean processCommand(HdmiCecMessage cmd); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java index 61c9424..2eca42b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java @@ -18,6 +18,10 @@ package com.android.server.hdmi; import android.view.KeyEvent; +import libcore.util.EmptyArray; + +import java.util.Arrays; + /** * Helper class to translate android keycode to hdmi cec keycode and vice versa. */ @@ -156,46 +160,60 @@ final class HdmiCecKeycode { /** * A mapping between Android and CEC keycode. + * <p> + * Normal implementation of this looks like * - * <p>Normal implementation of this looks like * <pre> - * new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT); + * new KeycodeEntry(KeyEvent.KEYCODE_DPAD_CENTER, CEC_KEYCODE_SELECT); * </pre> - * <p>However, some keys in CEC requires additional parameter. - * In order to use parameterized cec key, add unique android keycode (existing or custom) - * corresponding to a pair of cec keycode and and its param. + * <p> + * However, some keys in CEC requires additional parameter. In order to use parameterized cec + * key, add unique android keycode (existing or custom) corresponding to a pair of cec keycode + * and and its param. + * * <pre> - * new KeycodeEntry(CUSTOME_ANDORID_KEY_1, CEC_KEYCODE_SELECT_BROADCAST_TYPE, - * UI_BROADCAST_TOGGLE_ALL); - * new KeycodeEntry(CUSTOME_ANDORID_KEY_2, CEC_KEYCODE_SELECT_BROADCAST_TYPE, - * UI_BROADCAST_ANALOGUE); + * new KeycodeEntry(CUSTOME_ANDORID_KEY_1, CEC_KEYCODE_SELECT_BROADCAST_TYPE, + * UI_BROADCAST_TOGGLE_ALL); + * new KeycodeEntry(CUSTOME_ANDORID_KEY_2, CEC_KEYCODE_SELECT_BROADCAST_TYPE, + * UI_BROADCAST_ANALOGUE); * </pre> */ private static class KeycodeEntry { private final int mAndroidKeycode; - private final int mCecKeycode; private final boolean mIsRepeatable; + private final byte[] mCecKeycodeAndParams; - private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) { + private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable, + byte[] cecParams) { mAndroidKeycode = androidKeycode; - mCecKeycode = cecKeycode; mIsRepeatable = isRepeatable; + mCecKeycodeAndParams = new byte[cecParams.length + 1]; + System.arraycopy(cecParams, 0, mCecKeycodeAndParams, 1, cecParams.length); + mCecKeycodeAndParams[0] = (byte) (cecKeycode & 0xFF); + } + + private KeycodeEntry(int androidKeycode, int cecKeycode, boolean isRepeatable) { + this(androidKeycode, cecKeycode, isRepeatable, EmptyArray.BYTE); + } + + private KeycodeEntry(int androidKeycode, int cecKeycode, byte[] cecParams) { + this(androidKeycode, cecKeycode, true, cecParams); } private KeycodeEntry(int androidKeycode, int cecKeycode) { - this(androidKeycode, cecKeycode, true); + this(androidKeycode, cecKeycode, true, EmptyArray.BYTE); } - private int toCecKeycodeIfMatched(int androidKeycode) { + private byte[] toCecKeycodeAndParamIfMatched(int androidKeycode) { if (mAndroidKeycode == androidKeycode) { - return mCecKeycode; + return mCecKeycodeAndParams; } else { - return UNSUPPORTED_KEYCODE; + return null; } } - private int toAndroidKeycodeIfMatched(int cecKeycode) { - if (cecKeycode == mCecKeycode) { + private int toAndroidKeycodeIfMatched(byte[] cecKeycodeAndParams) { + if (Arrays.equals(mCecKeycodeAndParams, cecKeycodeAndParams)) { return mAndroidKeycode; } else { return UNSUPPORTED_KEYCODE; @@ -211,6 +229,11 @@ final class HdmiCecKeycode { } } + private static byte[] intToSingleByteArray(int value) { + return new byte[] { + (byte) (value & 0xFF) }; + } + // Keycode entry container for all mappings. // Note that order of entry is the same as above cec keycode definition. private static final KeycodeEntry[] KEYCODE_ENTRIES = new KeycodeEntry[] { @@ -227,19 +250,25 @@ final class HdmiCecKeycode { new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_UP), // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_DOWN), - new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU, false), - new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU, false), - new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_CONTENTS_MENU, false), + new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU), + new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU), + new KeycodeEntry(KeyEvent.KEYCODE_TV_CONTENTS_MENU, CEC_KEYCODE_CONTENTS_MENU, false), // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_FAVORITE_MENU), + // Note that both BACK and ESCAPE are mapped to EXIT of CEC keycode. + // This would be problematic when translates CEC keycode to Android keycode. + // In current implementation, we pick BACK as mapping of EXIT key. + // If you'd like to map CEC EXIT to Android EXIT key, change order of + // the following two definition. new KeycodeEntry(KeyEvent.KEYCODE_BACK, CEC_KEYCODE_EXIT), + new KeycodeEntry(KeyEvent.KEYCODE_ESCAPE, CEC_KEYCODE_EXIT), // RESERVED new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_TOP_MENU, CEC_KEYCODE_MEDIA_TOP_MENU), - // No Android keycode defined for CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU), + new KeycodeEntry(KeyEvent.KEYCODE_TV_MEDIA_CONTEXT_MENU, + CEC_KEYCODE_MEDIA_CONTEXT_SENSITIVE_MENU), // RESERVED // No Android keycode defined for CEC_KEYCODE_NUMBER_ENTRY_MODE - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_NUMBER_ENTRY_MODE), + new KeycodeEntry(KeyEvent.KEYCODE_TV_NUMBER_ENTRY, CEC_KEYCODE_NUMBER_ENTRY_MODE), new KeycodeEntry(KeyEvent.KEYCODE_11, CEC_KEYCODE_NUMBER_11), new KeycodeEntry(KeyEvent.KEYCODE_12, CEC_KEYCODE_NUMBER_12), new KeycodeEntry(KeyEvent.KEYCODE_0, CEC_KEYCODE_NUMBER_0_OR_NUMBER_10), @@ -276,7 +305,12 @@ final class HdmiCecKeycode { new KeycodeEntry(KeyEvent.KEYCODE_VOLUME_MUTE, CEC_KEYCODE_MUTE, false), new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY, CEC_KEYCODE_PLAY), new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_STOP, CEC_KEYCODE_STOP), + // Note that we map both MEDIA_PAUSE and MEDIA_PLAY_PAUSE to CEC PAUSE key. + // When it translates CEC PAUSE key, it picks Android MEDIA_PAUSE key as a mapping of + // it. If you'd like to choose MEDIA_PLAY_PAUSE, please change order of the following + // two lines. new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PAUSE, CEC_KEYCODE_PAUSE), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, CEC_KEYCODE_PAUSE), new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_RECORD, CEC_KEYCODE_RECORD), new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_REWIND, CEC_KEYCODE_REWIND), new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, CEC_KEYCODE_FAST_FORWARD), @@ -291,48 +325,61 @@ final class HdmiCecKeycode { new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESERVED), // No Android keycode defined for CEC_KEYCODE_ANGLE new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_ANGLE), - // No Android keycode defined for CEC_KEYCODE_SUB_PICTURE - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SUB_PICTURE), + new KeycodeEntry(KeyEvent.KEYCODE_CAPTIONS, CEC_KEYCODE_SUB_PICTURE), // No Android keycode defined for CEC_KEYCODE_VIDEO_ON_DEMAND new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_VIDEO_ON_DEMAND), new KeycodeEntry(KeyEvent.KEYCODE_GUIDE, CEC_KEYCODE_ELECTRONIC_PROGRAM_GUIDE), - // No Android keycode defined for CEC_KEYCODE_TIMER_PROGRAMMING - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TIMER_PROGRAMMING), + new KeycodeEntry(KeyEvent.KEYCODE_TV_TIMER_PROGRAMMING, CEC_KEYCODE_TIMER_PROGRAMMING), // No Android keycode defined for CEC_KEYCODE_INITIAL_CONFIGURATION new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_INITIAL_CONFIGURATION), // No Android keycode defined for CEC_KEYCODE_SELECT_BROADCAST_TYPE new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_BROADCAST_TYPE), + new KeycodeEntry(KeyEvent.KEYCODE_TV_TERRESTRIAL_ANALOG, + CEC_KEYCODE_SELECT_BROADCAST_TYPE, true, + intToSingleByteArray(UI_BROADCAST_ANALOGUE)), + new KeycodeEntry(KeyEvent.KEYCODE_TV_TERRESTRIAL_DIGITAL, + CEC_KEYCODE_SELECT_BROADCAST_TYPE, true, + intToSingleByteArray(UI_BROADCAST_DIGITAL_TERRESTRIAL)), + new KeycodeEntry(KeyEvent.KEYCODE_TV_SATELLITE_BS, + CEC_KEYCODE_SELECT_BROADCAST_TYPE, true, + intToSingleByteArray(UI_BROADCAST_DIGITAL_SATELLITE)), + new KeycodeEntry(KeyEvent.KEYCODE_TV_SATELLITE_CS, + CEC_KEYCODE_SELECT_BROADCAST_TYPE, true, + intToSingleByteArray(UI_BROADCAST_DIGITAL_COMMNICATIONS_SATELLITE)), + new KeycodeEntry(KeyEvent.KEYCODE_TV_NETWORK, + CEC_KEYCODE_SELECT_BROADCAST_TYPE, true, + intToSingleByteArray(UI_BROADCAST_TOGGLE_ANALOGUE_DIGITAL)), // No Android keycode defined for CEC_KEYCODE_SELECT_SOUND_PRESENTATION new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_SOUND_PRESENTATION), // RESERVED // The following deterministic key definitions do not need key mapping // since they are supposed to be generated programmatically only. // No Android keycode defined for CEC_KEYCODE_PLAY_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PLAY_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PLAY_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_PAUSE_PLAY_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_PLAY_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_PLAY_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_RECORD_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RECORD_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RECORD_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_PAUSE_RECORD_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_PAUSE_RECORD_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_STOP_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_STOP_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_MUTE_FUNCTION new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_MUTE_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_RESTORE_VOLUME_FUNCTION new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_RESTORE_VOLUME_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_TUNE_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TUNE_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_TUNE_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_SELECT_MEDIA_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_MEDIA_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_MEDIA_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AV_INPUT_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SELECT_AUDIO_INPUT_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_POWER_TOGGLE_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_TOGGLE_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_TOGGLE_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_POWER_OFF_FUNCTION - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_OFF_FUNCTION), + new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_OFF_FUNCTION, false), // No Android keycode defined for CEC_KEYCODE_POWER_ON_FUNCTION new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_POWER_ON_FUNCTION, false), // RESERVED @@ -347,31 +394,31 @@ final class HdmiCecKeycode { }; /** - * Translate Android keycode to Hdmi Cec keycode. + * Translate Android keycode to Hdmi Cec keycode and params. * * @param keycode Android keycode. For details, refer {@link KeyEvent} - * @return single byte CEC keycode if matched. + * @return byte array of CEC keycode and params if matched. Otherwise, return null. */ - static int androidKeyToCecKey(int keycode) { + static byte[] androidKeyToCecKey(int keycode) { for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) { - int cecKeycode = KEYCODE_ENTRIES[i].toCecKeycodeIfMatched(keycode); - if (cecKeycode != UNSUPPORTED_KEYCODE) { - return cecKeycode; + byte[] cecKeycodeAndParams = KEYCODE_ENTRIES[i].toCecKeycodeAndParamIfMatched(keycode); + if (cecKeycodeAndParams != null) { + return cecKeycodeAndParams; } } - return UNSUPPORTED_KEYCODE; + return null; } /** - * Translate Hdmi CEC keycode to Android keycode. + * Translate Hdmi CEC keycode with params to Android keycode. * - * @param keycode CEC keycode - * @return cec keycode corresponding to the given android keycode. - * If finds no matched keycode, return {@link #UNSUPPORTED_KEYCODE} + * @param cecKeycodeAndParams CEC keycode and params + * @return cec keycode corresponding to the given android keycode. If finds no matched keycode, + * return {@link #UNSUPPORTED_KEYCODE} */ - static int cecKeyToAndroidKey(int keycode) { + static int cecKeycodeAndParamsToAndroidKey(byte[] cecKeycodeAndParams) { for (int i = 0; i < KEYCODE_ENTRIES.length; ++i) { - int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(keycode); + int androidKey = KEYCODE_ENTRIES[i].toAndroidKeycodeIfMatched(cecKeycodeAndParams); if (androidKey != UNSUPPORTED_KEYCODE) { return androidKey; } @@ -399,7 +446,6 @@ final class HdmiCecKeycode { * Returns {@code true} if given Android keycode is supported, otherwise {@code false}. */ static boolean isSupportedKeycode(int androidKeycode) { - return HdmiCecKeycode.androidKeyToCecKey(androidKeycode) - != HdmiCecKeycode.UNSUPPORTED_KEYCODE; - } + return HdmiCecKeycode.androidKeyToCecKey(androidKeycode) != null; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 8f9af61..4f8b9fb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -34,7 +34,6 @@ import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; /** @@ -125,7 +124,7 @@ abstract class HdmiCecLocalDevice { // A collection of FeatureAction. // Note that access to this collection should happen in service thread. - private final LinkedList<HdmiCecFeatureAction> mActions = new LinkedList<>(); + private final ArrayList<HdmiCecFeatureAction> mActions = new ArrayList<>(); private final Handler mHandler = new Handler () { @Override @@ -290,12 +289,14 @@ abstract class HdmiCecLocalDevice { @ServiceThreadOnly private boolean dispatchMessageToAction(HdmiCecMessage message) { assertRunOnServiceThread(); - for (HdmiCecFeatureAction action : mActions) { - if (action.processCommand(message)) { - return true; - } + boolean processed = false; + // Use copied action list in that processCommand may remove itself. + for (HdmiCecFeatureAction action : new ArrayList<>(mActions)) { + // Iterates all actions to check whether incoming message is consumed. + boolean result = action.processCommand(message); + processed = processed || result; } - return false; + return processed; } @ServiceThreadOnly @@ -425,9 +426,7 @@ abstract class HdmiCecLocalDevice { final long downTime = SystemClock.uptimeMillis(); final byte[] params = message.getParams(); - // Note that we don't support parameterized keycode now. - // TODO: translate parameterized keycode as well. - final int keycode = HdmiCecKeycode.cecKeyToAndroidKey(params[0]); + final int keycode = HdmiCecKeycode.cecKeycodeAndParamsToAndroidKey(params); int keyRepeatCount = 0; if (mLastKeycode != HdmiCecKeycode.UNSUPPORTED_KEYCODE) { if (keycode == mLastKeycode) { @@ -517,8 +516,8 @@ abstract class HdmiCecLocalDevice { } protected boolean handleVendorCommand(HdmiCecMessage message) { - if (!mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), - message.getParams(), false)) { + if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(), + message.getDestination(), message.getParams(), false)) { // Vendor command listener may not have been registered yet. Respond with // <Feature Abort> [NOT_IN_CORRECT_MODE] so that the sender can try again later. mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); @@ -530,8 +529,8 @@ abstract class HdmiCecLocalDevice { byte[] params = message.getParams(); int vendorId = HdmiUtils.threeBytesToInt(params); if (vendorId == mService.getVendorId()) { - if (!mService.invokeVendorCommandListeners(mDeviceType, message.getSource(), params, - true)) { + if (!mService.invokeVendorCommandListenersOnReceived(mDeviceType, message.getSource(), + message.getDestination(), params, true)) { mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE); } } else if (message.getDestination() != Constants.ADDR_BROADCAST && diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 780d54b..85a1a15 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -50,6 +50,8 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { assertRunOnServiceThread(); mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( mAddress, mService.getPhysicalAddress(), mDeviceType)); + mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand( + mAddress, mService.getVendorId())); startQueuedActions(); } @@ -156,7 +158,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { assertRunOnServiceThread(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); maySetActiveSource(physicalAddress); - maySendActiveSource(); + maySendActiveSource(message.getSource()); wakeUpIfActiveSource(); return true; // Broadcast message. } @@ -196,10 +198,13 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { } } - private void maySendActiveSource() { + private void maySendActiveSource(int dest) { if (mIsActiveSource) { mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource( mAddress, mService.getPhysicalAddress())); + // Always reports menu-status active to receive RCP. + mService.sendCecCommand(HdmiCecMessageBuilder.buildReportMenuStatus( + mAddress, dest, Constants.MENU_STATE_ACTIVATED)); } } @@ -207,7 +212,7 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { @ServiceThreadOnly protected boolean handleRequestActiveSource(HdmiCecMessage message) { assertRunOnServiceThread(); - maySendActiveSource(); + maySendActiveSource(message.getSource()); return true; // Broadcast message. } @@ -230,4 +235,4 @@ final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice { super.dump(pw); pw.println("mIsActiveSource: " + mIsActiveSource); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 0fb4b48..6bae761 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -110,6 +110,9 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // If true, TV wakes itself up when receiving <Text/Image View On>. private boolean mAutoWakeup; + // List of the logical address of local CEC devices. Unmodifiable, thread-safe. + private List<Integer> mLocalDeviceAddresses; + private final HdmiCecStandbyModeHandler mStandbyHandler; // If true, do not do routing control/send active source for internal source. @@ -141,10 +144,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mSkipRoutingControl = (reason == HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE); launchRoutingControl(reason != HdmiControlService.INITIATED_BY_ENABLE_CEC && reason != HdmiControlService.INITIATED_BY_BOOT_UP); + mLocalDeviceAddresses = initLocalDeviceAddresses(); launchDeviceDiscovery(); startQueuedActions(); } + + @ServiceThreadOnly + private List<Integer> initLocalDeviceAddresses() { + assertRunOnServiceThread(); + List<Integer> addresses = new ArrayList<>(); + for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) { + addresses.add(device.getDeviceInfo().getLogicalAddress()); + } + return Collections.unmodifiableList(addresses); + } + @Override @ServiceThreadOnly protected int getPreferredAddress() { @@ -351,13 +366,26 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (!action.isEmpty()) { action.get(0).processKeyEvent(keyCode, isPressed); } else { - if (isPressed && getActiveSource().isValid()) { - int logicalAddress = getActiveSource().logicalAddress; - addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode)); - } else { - Slog.w(TAG, "Discard key event: " + keyCode + " pressed:" + isPressed); + if (isPressed) { + int logicalAddress = findKeyReceiverAddress(); + if (logicalAddress != Constants.ADDR_INVALID) { + addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode)); + return; + } } + Slog.w(TAG, "Discard key event: " + keyCode + " pressed:" + isPressed); + } + } + + private int findKeyReceiverAddress() { + if (getActiveSource().isValid()) { + return getActiveSource().logicalAddress; + } + HdmiDeviceInfo info = getDeviceInfoByPath(getActivePath()); + if (info != null) { + return info.getLogicalAddress(); } + return Constants.ADDR_INVALID; } private static void invokeCallback(IHdmiControlCallback callback, int result) { @@ -377,11 +405,12 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { assertRunOnServiceThread(); int logicalAddress = message.getSource(); int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); - if (getCecDeviceInfo(logicalAddress) == null) { + HdmiDeviceInfo info = getCecDeviceInfo(logicalAddress); + if (info == null) { handleNewDeviceAtTheTailOfActivePath(physicalAddress); } else { ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress); - ActiveSourceHandler.create(this, null).process(activeSource); + ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType()); } return true; } @@ -471,7 +500,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { if (!isInDeviceList(address, path)) { handleNewDeviceAtTheTailOfActivePath(path); } - startNewDeviceAction(ActiveSource.of(address, path)); + startNewDeviceAction(ActiveSource.of(address, path), type); return true; } @@ -507,7 +536,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { return false; } - void startNewDeviceAction(ActiveSource activeSource) { + void startNewDeviceAction(ActiveSource activeSource, int deviceType) { for (NewDeviceAction action : getActions(NewDeviceAction.class)) { // If there is new device action which has the same logical address and path // ignore new request. @@ -523,7 +552,7 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } addAndStartAction(new NewDeviceAction(this, activeSource.logicalAddress, - activeSource.physicalAddress)); + activeSource.physicalAddress, deviceType)); } private void handleNewDeviceAtTheTailOfActivePath(int path) { @@ -1182,15 +1211,8 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } - @ServiceThreadOnly private boolean isLocalDeviceAddress(int address) { - assertRunOnServiceThread(); - for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) { - if (device.isAddressOf(address)) { - return true; - } - } - return false; + return mLocalDeviceAddresses.contains(address); } @ServiceThreadOnly @@ -1240,6 +1262,17 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { } } + List<HdmiDeviceInfo> getSafeCecDevicesLocked() { + ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>(); + for (HdmiDeviceInfo info : mSafeAllDeviceInfos) { + if (isLocalDeviceAddress(info.getLogicalAddress())) { + continue; + } + infoList.add(info); + } + return infoList; + } + /** * Called when a device is newly added or a new device is detected or * existing device is updated. diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 60d1520..aeb21db 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -90,6 +90,8 @@ import java.util.Locale; */ public final class HdmiControlService extends SystemService { private static final String TAG = "HdmiControlService"; + private final Locale HONG_KONG = new Locale("zh", "HK"); + private final Locale MACAU = new Locale("zh", "MO"); static final String PERMISSION = "android.permission.HDMI_CEC"; @@ -146,13 +148,25 @@ public final class HdmiControlService extends SystemService { } break; case Intent.ACTION_CONFIGURATION_CHANGED: - String language = Locale.getDefault().getISO3Language(); + String language = getMenuLanguage(); if (!mLanguage.equals(language)) { onLanguageChanged(language); } break; } } + + private String getMenuLanguage() { + Locale locale = Locale.getDefault(); + if (locale.equals(Locale.TAIWAN) || locale.equals(HONG_KONG) || locale.equals(MACAU)) { + // Android always returns "zho" for all Chinese variants. + // Use "bibliographic" code defined in CEC639-2 for traditional + // Chinese used in Taiwan/Hong Kong/Macau. + return "chi"; + } else { + return locale.getISO3Language(); + } + } } // A thread to handle synchronous IO of CEC and MHL control service. @@ -303,6 +317,7 @@ public final class HdmiControlService extends SystemService { } } else { Slog.i(TAG, "Device does not support HDMI-CEC."); + return; } mMhlController = HdmiMhlControllerStub.create(this); @@ -315,20 +330,23 @@ public final class HdmiControlService extends SystemService { mMessageValidator = new HdmiCecMessageValidator(this); publishBinderService(Context.HDMI_CONTROL_SERVICE, new BinderService()); - // Register broadcast receiver for power state change. if (mCecController != null) { + // Register broadcast receiver for power state change. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); getContext().registerReceiver(mHdmiControlBroadcastReceiver, filter); + + // Register ContentObserver to monitor the settings change. + registerContentObserver(); } } /** * Called when the initialization of local devices is complete. */ - private void onInitializeCecComplete() { + private void onInitializeCecComplete(int initiatedBy) { if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) { mPowerStatus = HdmiControlManager.POWER_STATUS_ON; } @@ -336,7 +354,22 @@ public final class HdmiControlService extends SystemService { if (isTvDevice()) { mCecController.setOption(OPTION_CEC_AUTO_WAKEUP, toInt(tv().getAutoWakeup())); - registerContentObserver(); + } + int reason = -1; + switch (initiatedBy) { + case INITIATED_BY_BOOT_UP: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_START; + break; + case INITIATED_BY_ENABLE_CEC: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING; + break; + case INITIATED_BY_SCREEN_ON: + case INITIATED_BY_WAKE_UP_MESSAGE: + reason = HdmiControlManager.CONTROL_STATE_CHANGED_REASON_WAKEUP; + break; + } + if (reason != -1) { + invokeVendorCommandListenersOnControlStateChanged(true, reason); } } @@ -401,10 +434,6 @@ public final class HdmiControlService extends SystemService { Global.putInt(cr, key, toInt(value)); } - private void unregisterSettingsObserver() { - getContext().getContentResolver().unregisterContentObserver(mSettingsObserver); - } - private void initializeCec(int initiatedBy) { mCecController.setOption(OPTION_CEC_SERVICE_CONTROL, ENABLED); initializeLocalDevices(initiatedBy); @@ -459,7 +488,7 @@ public final class HdmiControlService extends SystemService { if (initiatedBy != INITIATED_BY_HOTPLUG) { // In case of the hotplug we don't call onInitializeCecComplete() // since we reallocate the logical address only. - onInitializeCecComplete(); + onInitializeCecComplete(initiatedBy); } notifyAddressAllocated(allocatedDevices, initiatedBy); } @@ -729,20 +758,18 @@ public final class HdmiControlService extends SystemService { void onHotplug(int portId, boolean connected) { assertRunOnServiceThread(); - ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>(); - for (int type : mLocalDevices) { - if (type == HdmiDeviceInfo.DEVICE_TV) { - // Skip the reallocation of the logical address on TV. - continue; - } - HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type); - if (localDevice == null) { - localDevice = HdmiCecLocalDevice.create(this, type); - localDevice.init(); + if (connected && !isTvDevice()) { + ArrayList<HdmiCecLocalDevice> localDevices = new ArrayList<>(); + for (int type : mLocalDevices) { + HdmiCecLocalDevice localDevice = mCecController.getLocalDevice(type); + if (localDevice == null) { + localDevice = HdmiCecLocalDevice.create(this, type); + localDevice.init(); + } + localDevices.add(localDevice); } - localDevices.add(localDevice); + allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG); } - allocateLogicalAddress(localDevices, INITIATED_BY_HOTPLUG); for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { device.onHotplug(portId, connected); @@ -827,6 +854,7 @@ public final class HdmiControlService extends SystemService { @ServiceThreadOnly void handleMhlHotplugEvent(int portId, boolean connected) { assertRunOnServiceThread(); + // Hotplug event is used to add/remove MHL devices as TV input. if (connected) { HdmiMhlLocalDeviceStub newDevice = new HdmiMhlLocalDeviceStub(this, portId); HdmiMhlLocalDeviceStub oldDevice = mMhlController.addLocalDevice(newDevice); @@ -834,17 +862,14 @@ public final class HdmiControlService extends SystemService { oldDevice.onDeviceRemoved(); Slog.i(TAG, "Old device of port " + portId + " is removed"); } + invokeDeviceEventListeners(newDevice.getInfo(), DEVICE_EVENT_ADD_DEVICE); + updateSafeMhlInput(); } else { HdmiMhlLocalDeviceStub device = mMhlController.removeLocalDevice(portId); if (device != null) { device.onDeviceRemoved(); - // There is no explicit event for device removal. - // Hence we remove the device on hotplug event. - HdmiDeviceInfo deviceInfo = device.getInfo(); - if (deviceInfo != null) { - invokeDeviceEventListeners(deviceInfo, DEVICE_EVENT_REMOVE_DEVICE); - updateSafeMhlInput(); - } + invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_REMOVE_DEVICE); + updateSafeMhlInput(); } else { Slog.w(TAG, "No device to remove:[portId=" + portId); } @@ -880,11 +905,8 @@ public final class HdmiControlService extends SystemService { assertRunOnServiceThread(); HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId); - // Hotplug event should already have been called before device status change event. if (device != null) { device.setDeviceStatusChange(adopterId, deviceId); - invokeDeviceEventListeners(device.getInfo(), DEVICE_EVENT_ADD_DEVICE); - updateSafeMhlInput(); } else { Slog.w(TAG, "No mhl device exists for device status event[portId:" + portId + ", adopterId:" + adopterId + ", deviceId:" + deviceId + "]"); @@ -1025,6 +1047,7 @@ public final class HdmiControlService extends SystemService { @Override public HdmiDeviceInfo getActiveSource() { + enforceAccessPermission(); HdmiCecLocalDeviceTv tv = tv(); if (tv == null) { Slog.w(TAG, "Local tv device not available"); @@ -1238,6 +1261,19 @@ public final class HdmiControlService extends SystemService { } } + // Returns all the CEC devices on the bus including system audio, switch, + // even those of reserved type. + @Override + public List<HdmiDeviceInfo> getDeviceList() { + enforceAccessPermission(); + HdmiCecLocalDeviceTv tv = tv(); + synchronized (mLock) { + return (tv == null) + ? Collections.<HdmiDeviceInfo>emptyList() + : tv.getSafeCecDevicesLocked(); + } + } + @Override public void setSystemAudioVolume(final int oldIndex, final int newIndex, final int maxIndex) { @@ -1344,11 +1380,13 @@ public final class HdmiControlService extends SystemService { @Override public void setHdmiRecordListener(IHdmiRecordListener listener) { + enforceAccessPermission(); HdmiControlService.this.setHdmiRecordListener(listener); } @Override public void startOneTouchRecord(final int recorderAddress, final byte[] recordSource) { + enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { @@ -1363,6 +1401,7 @@ public final class HdmiControlService extends SystemService { @Override public void stopOneTouchRecord(final int recorderAddress) { + enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { @@ -1378,6 +1417,7 @@ public final class HdmiControlService extends SystemService { @Override public void startTimerRecording(final int recorderAddress, final int sourceType, final byte[] recordSource) { + enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { @@ -1393,6 +1433,7 @@ public final class HdmiControlService extends SystemService { @Override public void clearTimerRecording(final int recorderAddress, final int sourceType, final byte[] recordSource) { + enforceAccessPermission(); runOnServiceThread(new Runnable() { @Override public void run() { @@ -1696,7 +1737,7 @@ public final class HdmiControlService extends SystemService { } boolean isTvDevice() { - return tv() != null; + return mLocalDevices.contains(HdmiDeviceInfo.DEVICE_TV); } private HdmiCecLocalDevicePlayback playback() { @@ -1782,6 +1823,8 @@ public final class HdmiControlService extends SystemService { private void onStandby() { assertRunOnServiceThread(); mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + invokeVendorCommandListenersOnControlStateChanged(false, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_STANDBY); final List<HdmiCecLocalDevice> devices = getAllLocalDevices(); disableDevices(new PendingActionClearedCallback() { @@ -1820,9 +1863,6 @@ public final class HdmiControlService extends SystemService { for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { device.disableDevice(mStandbyMessageReceived, callback); } - if (isTvDevice()) { - unregisterSettingsObserver(); - } } mMhlController.clearAllLocalDevices(); @@ -1867,8 +1907,8 @@ public final class HdmiControlService extends SystemService { } } - boolean invokeVendorCommandListeners(int deviceType, int srcAddress, byte[] params, - boolean hasVendorId) { + boolean invokeVendorCommandListenersOnReceived(int deviceType, int srcAddress, int destAddress, + byte[] params, boolean hasVendorId) { synchronized (mLock) { if (mVendorCommandListenerRecords.isEmpty()) { return false; @@ -1878,7 +1918,7 @@ public final class HdmiControlService extends SystemService { continue; } try { - record.mListener.onReceived(srcAddress, params, hasVendorId); + record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId); } catch (RemoteException e) { Slog.e(TAG, "Failed to notify vendor command reception", e); } @@ -1887,6 +1927,22 @@ public final class HdmiControlService extends SystemService { } } + boolean invokeVendorCommandListenersOnControlStateChanged(boolean enabled, int reason) { + synchronized (mLock) { + if (mVendorCommandListenerRecords.isEmpty()) { + return false; + } + for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) { + try { + record.mListener.onControlStateChanged(enabled, reason); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to notify control-state-changed to vendor handler", e); + } + } + return true; + } + } + private void addHdmiMhlVendorCommandListener(IHdmiMhlVendorCommandListener listener) { HdmiMhlVendorCommandListenerRecord record = new HdmiMhlVendorCommandListenerRecord(listener); @@ -1936,6 +1992,11 @@ public final class HdmiControlService extends SystemService { void setControlEnabled(boolean enabled) { assertRunOnServiceThread(); + if (!enabled) { + // Call the vendor handler before the service is disabled. + invokeVendorCommandListenersOnControlStateChanged(false, + HdmiControlManager.CONTROL_STATE_CHANGED_REASON_SETTING); + } int value = toInt(enabled); mCecController.setOption(OPTION_CEC_ENABLE, value); mMhlController.setOption(OPTION_MHL_ENABLE, value); @@ -2008,9 +2069,7 @@ public final class HdmiControlService extends SystemService { // may not be the MHL-enabled one. In this case the device info to be passed to // input change listener should be the one describing the corresponding HDMI port. HdmiMhlLocalDeviceStub device = mMhlController.getLocalDevice(portId); - HdmiDeviceInfo info = (device != null && device.getInfo() != null) - ? device.getInfo() - : mPortDeviceMap.get(portId); + HdmiDeviceInfo info = (device != null) ? device.getInfo() : mPortDeviceMap.get(portId); invokeInputChangeListener(info); } @@ -2042,7 +2101,7 @@ public final class HdmiControlService extends SystemService { assertRunOnServiceThread(); Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE); intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId); - intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRAM_PARAM1, extra); + intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra); getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION); } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index 2074085..998889b 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -47,6 +47,7 @@ final class NewDeviceAction extends HdmiCecFeatureAction { private final int mDeviceLogicalAddress; private final int mDevicePhysicalAddress; + private final int mDeviceType; private int mVendorId; private String mDisplayName; @@ -57,12 +58,14 @@ final class NewDeviceAction extends HdmiCecFeatureAction { * @param source {@link HdmiCecLocalDevice} instance * @param deviceLogicalAddress logical address of the device in interest * @param devicePhysicalAddress physical address of the device in interest + * @param deviceType type of the device */ NewDeviceAction(HdmiCecLocalDevice source, int deviceLogicalAddress, - int devicePhysicalAddress) { + int devicePhysicalAddress, int deviceType) { super(source); mDeviceLogicalAddress = deviceLogicalAddress; mDevicePhysicalAddress = devicePhysicalAddress; + mDeviceType = deviceType; mVendorId = Constants.UNKNOWN_VENDOR_ID; } @@ -155,8 +158,7 @@ final class NewDeviceAction extends HdmiCecFeatureAction { HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo( mDeviceLogicalAddress, mDevicePhysicalAddress, tv().getPortId(mDevicePhysicalAddress), - HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress), - mVendorId, mDisplayName); + mDeviceType, mVendorId, mDisplayName); tv().addCecDevice(deviceInfo); if (HdmiUtils.getTypeFromAddress(mDeviceLogicalAddress) diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java index 7db991a..e764a1c 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java @@ -92,7 +92,8 @@ final class OneTouchPlayAction extends HdmiCecFeatureAction { @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAITING_FOR_REPORT_POWER_STATUS) { + if (mState != STATE_WAITING_FOR_REPORT_POWER_STATUS + || mTargetAddress != cmd.getSource()) { return false; } if (cmd.getOpcode() == Constants.MESSAGE_REPORT_POWER_STATUS) { diff --git a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java index 39c0d7f..906944b 100644 --- a/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java +++ b/services/core/java/com/android/server/hdmi/OneTouchRecordAction.java @@ -68,23 +68,21 @@ public class OneTouchRecordAction extends HdmiCecFeatureAction { finish(); return; } - - mState = STATE_WAITING_FOR_RECORD_STATUS; - addTimer(mState, RECORD_STATUS_TIMEOUT_MS); } }); + mState = STATE_WAITING_FOR_RECORD_STATUS; + addTimer(mState, RECORD_STATUS_TIMEOUT_MS); } @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAITING_FOR_RECORD_STATUS) { + if (mState != STATE_WAITING_FOR_RECORD_STATUS || mRecorderAddress != cmd.getSource()) { return false; } switch (cmd.getOpcode()) { case Constants.MESSAGE_RECORD_STATUS: return handleRecordStatus(cmd); - } return false; } diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java index ed978e0..eef5010 100644 --- a/services/core/java/com/android/server/hdmi/SendKeyAction.java +++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java @@ -15,7 +15,7 @@ */ package com.android.server.hdmi; -import static com.android.server.hdmi.Constants.IRT_MS; +import static com.android.server.hdmi.HdmiConfig.IRT_MS; import android.util.Slog; import android.view.KeyEvent; @@ -35,6 +35,11 @@ import android.view.KeyEvent; final class SendKeyAction extends HdmiCecFeatureAction { private static final String TAG = "SendKeyAction"; + // Amount of time this action waits for a new release key input event. When timed out, + // the action sends out UCR and finishes its lifecycle. Used to deal with missing key release + // event, which can lead the device on the receiving end to generating unintended key repeats. + private static final int AWAIT_RELEASE_KEY_MS = 1000; + // State in which the action is at work. The state is set in {@link #start()} and // persists throughout the process till it is set back to {@code STATE_NONE} at the end. private static final int STATE_PROCESSING_KEYCODE = 1; @@ -45,6 +50,10 @@ final class SendKeyAction extends HdmiCecFeatureAction { // The key code of the last key press event the action is passed via processKeyEvent. private int mLastKeycode; + // The time stamp when the last CEC key command was sent. Used to determine the press-and-hold + // operation. + private long mLastSendKeyTime; + /** * Constructor. * @@ -61,6 +70,7 @@ final class SendKeyAction extends HdmiCecFeatureAction { @Override public boolean start() { sendKeyDown(mLastKeycode); + mLastSendKeyTime = getCurrentTime(); // finish action for non-repeatable key. if (!HdmiCecKeycode.isRepeatableKey(mLastKeycode)) { sendKeyUp(); @@ -68,10 +78,14 @@ final class SendKeyAction extends HdmiCecFeatureAction { return true; } mState = STATE_PROCESSING_KEYCODE; - addTimer(mState, IRT_MS); + addTimer(mState, AWAIT_RELEASE_KEY_MS); return true; } + private long getCurrentTime() { + return System.currentTimeMillis(); + } + /** * Called when a key event should be handled for the action. * @@ -83,24 +97,32 @@ final class SendKeyAction extends HdmiCecFeatureAction { Slog.w(TAG, "Not in a valid state"); return; } - // A new key press event that comes in with a key code different from the last - // one sets becomes a new key code to be used for press-and-hold operation. - // Removes any pending timer and starts a new timer for itself. - // Key release event indicates that the action shall be finished. Send UCR - // command and terminate the action. Other release events are ignored. if (isPressed) { + // A new key press event that comes in with a key code different from the last + // one becomes a new key code to be used for press-and-hold operation. if (keycode != mLastKeycode) { sendKeyDown(keycode); + mLastSendKeyTime = getCurrentTime(); if (!HdmiCecKeycode.isRepeatableKey(keycode)) { sendKeyUp(); finish(); return; } - mActionTimer.clearTimerMessage(); - addTimer(mState, IRT_MS); - mLastKeycode = keycode; + } else { + // Press-and-hold key transmission takes place if Android key inputs are + // repeatedly coming in and more than IRT_MS has passed since the last + // press-and-hold key transmission. + if (getCurrentTime() - mLastSendKeyTime >= IRT_MS) { + sendKeyDown(keycode); + mLastSendKeyTime = getCurrentTime(); + } } + mActionTimer.clearTimerMessage(); + addTimer(mState, AWAIT_RELEASE_KEY_MS); + mLastKeycode = keycode; } else { + // Key release event indicates that the action shall be finished. Send UCR + // command and terminate the action. Other release events are ignored. if (keycode == mLastKeycode) { sendKeyUp(); finish(); @@ -109,12 +131,12 @@ final class SendKeyAction extends HdmiCecFeatureAction { } private void sendKeyDown(int keycode) { - int cecKeycode = HdmiCecKeycode.androidKeyToCecKey(keycode); - if (cecKeycode == HdmiCecKeycode.UNSUPPORTED_KEYCODE) { + byte[] cecKeycodeAndParams = HdmiCecKeycode.androidKeyToCecKey(keycode); + if (cecKeycodeAndParams == null) { return; } sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(getSourceAddress(), - mTargetAddress, new byte[] { (byte) (cecKeycode & 0xFF) })); + mTargetAddress, cecKeycodeAndParams)); } private void sendKeyUp() { @@ -130,15 +152,12 @@ final class SendKeyAction extends HdmiCecFeatureAction { @Override public void handleTimerEvent(int state) { - // Timer event occurs every IRT_MS milliseconds to perform key-repeat (or press-and-hold) - // operation. If the last received key code is as same as the one with which the action - // is started, plus there was no key release event in last IRT_MS timeframe, send a UCP - // command and start another timer to schedule the next press-and-hold command. + // Timeout on waiting for the release key event. Send UCR and quit the action. if (mState != STATE_PROCESSING_KEYCODE) { Slog.w(TAG, "Not in a valid state"); return; } - sendKeyDown(mLastKeycode); - addTimer(mState, IRT_MS); + sendKeyUp(); + finish(); } } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java index 0871194..6023354 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -125,6 +125,9 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction { @Override final boolean processCommand(HdmiCecMessage cmd) { + if (cmd.getSource() != mAvrLogicalAddress) { + return false; + } switch (mState) { case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE: if (cmd.getOpcode() == Constants.MESSAGE_FEATURE_ABORT diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java index 3653aac..50f8475 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java @@ -58,17 +58,16 @@ final class SystemAudioAutoInitiationAction extends HdmiCecFeatureAction { @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS) { + if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS + || mAvrAddress != cmd.getSource()) { return false; } - switch (cmd.getOpcode()) { - case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS: - handleSystemAudioModeStatusMessage(); - return true; - default: - return false; + if (cmd.getOpcode() == Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS) { + handleSystemAudioModeStatusMessage(); + return true; } + return false; } private void handleSystemAudioModeStatusMessage() { diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java index bfcda50..dba3591 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java @@ -79,7 +79,7 @@ final class SystemAudioStatusAction extends HdmiCecFeatureAction { @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS) { + if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS || mAvrAddress != cmd.getSource()) { return false; } diff --git a/services/core/java/com/android/server/hdmi/TimerRecordingAction.java b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java index 8fc0182..5fcbc91 100644 --- a/services/core/java/com/android/server/hdmi/TimerRecordingAction.java +++ b/services/core/java/com/android/server/hdmi/TimerRecordingAction.java @@ -96,11 +96,8 @@ public class TimerRecordingAction extends HdmiCecFeatureAction { @Override boolean processCommand(HdmiCecMessage cmd) { - if (mState != STATE_WAITING_FOR_TIMER_STATUS) { - return false; - } - - if (cmd.getSource() != mRecorderAddress) { + if (mState != STATE_WAITING_FOR_TIMER_STATUS + || cmd.getSource() != mRecorderAddress) { return false; } diff --git a/services/core/java/com/android/server/hdmi/VolumeControlAction.java b/services/core/java/com/android/server/hdmi/VolumeControlAction.java index 4338fc7..cd38b1f 100644 --- a/services/core/java/com/android/server/hdmi/VolumeControlAction.java +++ b/services/core/java/com/android/server/hdmi/VolumeControlAction.java @@ -16,10 +16,10 @@ package com.android.server.hdmi; -import static com.android.server.hdmi.Constants.IRT_MS; import static com.android.server.hdmi.Constants.MESSAGE_FEATURE_ABORT; import static com.android.server.hdmi.Constants.MESSAGE_REPORT_AUDIO_STATUS; import static com.android.server.hdmi.Constants.MESSAGE_USER_CONTROL_PRESSED; +import static com.android.server.hdmi.HdmiConfig.IRT_MS; import android.media.AudioManager; @@ -45,6 +45,7 @@ final class VolumeControlAction extends HdmiCecFeatureAction { private boolean mIsVolumeUp; private long mLastKeyUpdateTime; private int mLastAvrVolume; + private boolean mLastAvrMute; private boolean mSentKeyPressed; /** @@ -74,6 +75,7 @@ final class VolumeControlAction extends HdmiCecFeatureAction { mAvrAddress = avrAddress; mIsVolumeUp = isVolumeUp; mLastAvrVolume = UNKNOWN_AVR_VOLUME; + mLastAvrMute = false; mSentKeyPressed = false; updateLastKeyUpdateTime(); @@ -108,6 +110,8 @@ final class VolumeControlAction extends HdmiCecFeatureAction { HdmiLogger.debug("Volume Key Status Changed[old:%b new:%b]", mIsVolumeUp, isVolumeUp); sendVolumeKeyReleased(); mIsVolumeUp = isVolumeUp; + sendVolumeKeyPressed(); + resetTimer(); } updateLastKeyUpdateTime(); } @@ -129,9 +133,8 @@ final class VolumeControlAction extends HdmiCecFeatureAction { return handleReportAudioStatus(cmd); case MESSAGE_FEATURE_ABORT: return handleFeatureAbort(cmd); - default: - return false; } + return false; } private boolean handleReportAudioStatus(HdmiCecMessage cmd) { @@ -139,9 +142,12 @@ final class VolumeControlAction extends HdmiCecFeatureAction { boolean mute = (params[0] & 0x80) == 0x80; int volume = params[0] & 0x7F; mLastAvrVolume = volume; + mLastAvrMute = mute; if (shouldUpdateAudioVolume(mute)) { HdmiLogger.debug("Force volume change[mute:%b, volume=%d]", mute, volume); tv().setAudioStatus(mute, volume); + mLastAvrVolume = UNKNOWN_AVR_VOLUME; + mLastAvrMute = false; } return true; } @@ -182,8 +188,9 @@ final class VolumeControlAction extends HdmiCecFeatureAction { sendVolumeKeyReleased(); } if (mLastAvrVolume != UNKNOWN_AVR_VOLUME) { - tv().setAudioStatus(false, mLastAvrVolume); + tv().setAudioStatus(mLastAvrMute, mLastAvrVolume); mLastAvrVolume = UNKNOWN_AVR_VOLUME; + mLastAvrMute = false; } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index c6d2db2..83d6986 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -137,11 +137,15 @@ public class JobSchedulerService extends com.android.server.SystemService public void onReceive(Context context, Intent intent) { Slog.d(TAG, "Receieved: " + intent.getAction()); if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (DEBUG) { - Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); + // If this is an outright uninstall rather than the first half of an + // app update sequence, cancel the jobs associated with the app. + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (DEBUG) { + Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); + } + cancelJobsForUid(uidRemoved); } - cancelJobsForUid(uidRemoved); } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); if (DEBUG) { diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 0198e46..c2cb4b1 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -473,14 +473,7 @@ public class GpsLocationProvider implements LocationProviderInterface { checkSmsSuplInit(intent); } else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) { checkWapSuplInit(intent); - } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { - int networkState; - if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) { - networkState = LocationProvider.TEMPORARILY_UNAVAILABLE; - } else { - networkState = LocationProvider.AVAILABLE; - } - + } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE)) { // retrieve NetworkInfo result for this UID NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); @@ -488,6 +481,15 @@ public class GpsLocationProvider implements LocationProviderInterface { mContext.getSystemService(Context.CONNECTIVITY_SERVICE); info = connManager.getNetworkInfo(info.getType()); + int networkState; + if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false) || + !info.isConnected()) { + networkState = LocationProvider.TEMPORARILY_UNAVAILABLE; + } else { + networkState = LocationProvider.AVAILABLE; + } + + updateNetworkState(networkState, info); } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action) || Intent.ACTION_SCREEN_OFF.equals(action) @@ -715,7 +717,7 @@ public class GpsLocationProvider implements LocationProviderInterface { intentFilter = new IntentFilter(); intentFilter.addAction(ALARM_WAKEUP); intentFilter.addAction(ALARM_TIMEOUT); - intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE); intentFilter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_SCREEN_ON); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 531d20a..e9b3f8b 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -326,7 +326,7 @@ public final class MediaProjectionManagerService extends SystemService final long token = Binder.clearCallingIdentity(); try { - dump(pw); + MediaProjectionManagerService.this.dump(pw); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index b5aa4d8..150ad34 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -45,7 +45,6 @@ import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_DEV_ROTATE_AGE; import static android.provider.Settings.Global.NETSTATS_GLOBAL_ALERT_BYTES; import static android.provider.Settings.Global.NETSTATS_POLL_INTERVAL; -import static android.provider.Settings.Global.NETSTATS_REPORT_XT_OVER_DEV; import static android.provider.Settings.Global.NETSTATS_SAMPLE_ENABLED; import static android.provider.Settings.Global.NETSTATS_TIME_CACHE_MAX_AGE; import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION; @@ -184,7 +183,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public long getPollInterval(); public long getTimeCacheMaxAge(); public boolean getSampleEnabled(); - public boolean getReportXtOverDev(); public static class Config { public final long bucketDuration; @@ -229,8 +227,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private NetworkStatsRecorder mUidRecorder; private NetworkStatsRecorder mUidTagRecorder; - /** Cached {@link #mDevRecorder} stats. */ - private NetworkStatsCollection mDevStatsCached; /** Cached {@link #mXtRecorder} stats. */ private NetworkStatsCollection mXtStatsCached; @@ -305,7 +301,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // read historical network stats from disk, since policy service // might need them right away. - mDevStatsCached = mDevRecorder.getOrLoadCompleteLocked(); mXtStatsCached = mXtRecorder.getOrLoadCompleteLocked(); // bootstrap initial stats to prevent double-counting later @@ -386,7 +381,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mUidRecorder = null; mUidTagRecorder = null; - mDevStatsCached = null; mXtStatsCached = null; mSystemReady = false; @@ -523,48 +517,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Return network summary, splicing between {@link #mDevStatsCached} - * and {@link #mXtStatsCached} when appropriate. + * Return network summary, splicing between DEV and XT stats when + * appropriate. */ private NetworkStats internalGetSummaryForNetwork( NetworkTemplate template, long start, long end) { - if (!mSettings.getReportXtOverDev()) { - // shortcut when XT reporting disabled - return mDevStatsCached.getSummary(template, start, end); - } - - // splice stats between DEV and XT, switching over from DEV to XT at - // first atomic bucket. - final long firstAtomicBucket = mXtStatsCached.getFirstAtomicBucketMillis(); - final NetworkStats dev = mDevStatsCached.getSummary( - template, Math.min(start, firstAtomicBucket), Math.min(end, firstAtomicBucket)); - final NetworkStats xt = mXtStatsCached.getSummary( - template, Math.max(start, firstAtomicBucket), Math.max(end, firstAtomicBucket)); - - xt.combineAllValues(dev); - return xt; + // We've been using pure XT stats long enough that we no longer need to + // splice DEV and XT together. + return mXtStatsCached.getSummary(template, start, end); } /** - * Return network history, splicing between {@link #mDevStatsCached} - * and {@link #mXtStatsCached} when appropriate. + * Return network history, splicing between DEV and XT stats when + * appropriate. */ private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields) { - if (!mSettings.getReportXtOverDev()) { - // shortcut when XT reporting disabled - return mDevStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields); - } - - // splice stats between DEV and XT, switching over from DEV to XT at - // first atomic bucket. - final long firstAtomicBucket = mXtStatsCached.getFirstAtomicBucketMillis(); - final NetworkStatsHistory dev = mDevStatsCached.getHistory( - template, UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, firstAtomicBucket); - final NetworkStatsHistory xt = mXtStatsCached.getHistory( - template, UID_ALL, SET_ALL, TAG_NONE, fields, firstAtomicBucket, Long.MAX_VALUE); - - xt.recordEntireHistory(dev); - return xt; + // We've been using pure XT stats long enough that we no longer need to + // splice DEV and XT together. + return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields); } @Override @@ -1329,10 +1299,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true); } @Override - public boolean getReportXtOverDev() { - return getGlobalBoolean(NETSTATS_REPORT_XT_OVER_DEV, true); - } - @Override public Config getDevConfig() { return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 97f0a1e..24fc455 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -20,6 +20,7 @@ public interface NotificationDelegate { void onSetDisabled(int status); void onClearAll(int callingUid, int callingPid, int userId); void onNotificationClick(int callingUid, int callingPid, String key); + void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex); void onNotificationClear(int callingUid, int callingPid, String pkg, String tag, int id, int userId); void onNotificationError(int callingUid, int callingPid, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 22f060f..42a8551 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -41,6 +41,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -541,6 +542,20 @@ public class NotificationManagerService extends SystemService { } @Override + public void onNotificationActionClick(int callingUid, int callingPid, String key, + int actionIndex) { + synchronized (mNotificationList) { + EventLogTags.writeNotificationActionClicked(key, actionIndex); + NotificationRecord r = mNotificationsByKey.get(key); + if (r == null) { + Log.w(TAG, "No notification with key: " + key); + return; + } + // TODO: Log action click via UsageStats. + } + } + + @Override public void onNotificationClear(int callingUid, int callingPid, String pkg, String tag, int id, int userId) { cancelNotification(callingUid, callingPid, pkg, tag, id, 0, @@ -643,7 +658,7 @@ public class NotificationManagerService extends SystemService { } }; - private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -659,6 +674,8 @@ public class NotificationManagerService extends SystemService { || (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED)) || (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART)) || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) { + int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, + UserHandle.USER_ALL); String pkgList[] = null; boolean queryReplace = queryRemove && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); @@ -679,8 +696,10 @@ public class NotificationManagerService extends SystemService { if (packageChanged) { // We cancel notifications for packages which have just been disabled try { - final int enabled = getContext().getPackageManager() - .getApplicationEnabledSetting(pkgName); + final IPackageManager pm = AppGlobals.getPackageManager(); + final int enabled = pm.getApplicationEnabledSetting(pkgName, + changeUserId != UserHandle.USER_ALL ? changeUserId : + UserHandle.USER_OWNER); if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { cancelNotifications = false; @@ -691,6 +710,8 @@ public class NotificationManagerService extends SystemService { if (DBG) { Slog.i(TAG, "Exception trying to look up app enabled setting", e); } + } catch (RemoteException e) { + // Failed to talk to PackageManagerService Should never happen! } } pkgList = new String[]{pkgName}; @@ -700,13 +721,22 @@ public class NotificationManagerService extends SystemService { for (String pkgName : pkgList) { if (cancelNotifications) { cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart, - UserHandle.USER_ALL, REASON_PACKAGE_CHANGED, null); + changeUserId, REASON_PACKAGE_CHANGED, null); } } } mListeners.onPackagesChanged(queryReplace, pkgList); mConditionProviders.onPackagesChanged(queryReplace, pkgList); - } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + } + } + }; + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (action.equals(Intent.ACTION_SCREEN_ON)) { // Keep track of screen on/off state, but do not turn off the notification light // until user passes through the lock screen or views the notification. mScreenOn = true; @@ -889,6 +919,7 @@ public class NotificationManagerService extends SystemService { filter.addAction(Intent.ACTION_USER_SWITCHED); filter.addAction(Intent.ACTION_USER_ADDED); getContext().registerReceiver(mIntentReceiver, filter); + IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -896,9 +927,12 @@ public class NotificationManagerService extends SystemService { pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); - getContext().registerReceiver(mIntentReceiver, pkgFilter); + getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null, + null); + IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - getContext().registerReceiver(mIntentReceiver, sdFilter); + getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null, + null); mSettingsObserver = new SettingsObserver(mHandler); @@ -1698,14 +1732,6 @@ public class NotificationManagerService extends SystemService { } } - // This conditional is a dirty hack to limit the logging done on - // behalf of the download manager without affecting other apps. - if (!pkg.equals("com.android.providers.downloads") - || Log.isLoggable("DownloadManager", Log.VERBOSE)) { - EventLogTags.writeNotificationEnqueue(callingUid, callingPid, - pkg, id, tag, userId, notification.toString()); - } - if (pkg == null || notification == null) { throw new IllegalArgumentException("null not allowed: pkg=" + pkg + " id=" + id + " notification=" + notification); @@ -1755,6 +1781,14 @@ public class NotificationManagerService extends SystemService { } mRankingHelper.extractSignals(r); + // This conditional is a dirty hack to limit the logging done on + // behalf of the download manager without affecting other apps. + if (!pkg.equals("com.android.providers.downloads") + || Log.isLoggable("DownloadManager", Log.VERBOSE)) { + EventLogTags.writeNotificationEnqueue(callingUid, callingPid, + pkg, id, tag, userId, notification.toString(), + (old != null) ? 1 : 0); + } // 3. Apply local rules // blocked apps @@ -2358,6 +2392,8 @@ public class NotificationManagerService extends SystemService { // Save it for users of getHistoricalNotifications() mArchive.record(r.sbn); + + EventLogTags.writeNotificationCanceled(r.getKey(), reason); } /** diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index a7eebf8..cb9a45e 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -23,9 +23,9 @@ import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.os.ServiceManager; +import android.util.ArraySet; import android.util.Log; -import java.util.HashSet; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -59,7 +59,7 @@ public class BackgroundDexOptService extends JobService { if (pm.isStorageLow()) { return false; } - final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt(); + final ArraySet<String> pkgs = pm.getPackagesThatNeedDexOpt(); if (pkgs == null) { return false; } diff --git a/services/core/java/com/android/server/pm/GrantedPermissions.java b/services/core/java/com/android/server/pm/GrantedPermissions.java index 14258a4..8f0f935 100644 --- a/services/core/java/com/android/server/pm/GrantedPermissions.java +++ b/services/core/java/com/android/server/pm/GrantedPermissions.java @@ -17,13 +17,12 @@ package com.android.server.pm; import android.content.pm.ApplicationInfo; - -import java.util.HashSet; +import android.util.ArraySet; class GrantedPermissions { int pkgFlags; - HashSet<String> grantedPermissions = new HashSet<String>(); + ArraySet<String> grantedPermissions = new ArraySet<String>(); int[] gids; @@ -34,7 +33,7 @@ class GrantedPermissions { @SuppressWarnings("unchecked") GrantedPermissions(GrantedPermissions base) { pkgFlags = base.pkgFlags; - grantedPermissions = (HashSet<String>) base.grantedPermissions.clone(); + grantedPermissions = new ArraySet<>(base.grantedPermissions); if (base.gids != null) { gids = base.gids.clone(); diff --git a/services/core/java/com/android/server/pm/PackageKeySetData.java b/services/core/java/com/android/server/pm/PackageKeySetData.java index 9f9bafd..8f12c03 100644 --- a/services/core/java/com/android/server/pm/PackageKeySetData.java +++ b/services/core/java/com/android/server/pm/PackageKeySetData.java @@ -16,10 +16,9 @@ package com.android.server.pm; -import com.android.internal.util.ArrayUtils; +import android.util.ArrayMap; -import java.util.HashMap; -import java.util.Map; +import com.android.internal.util.ArrayUtils; public class PackageKeySetData { @@ -34,7 +33,7 @@ public class PackageKeySetData { private long[] mDefinedKeySets; - private final Map<String, Long> mKeySetAliases = new HashMap<String, Long>(); + private final ArrayMap<String, Long> mKeySetAliases = new ArrayMap<String, Long>(); PackageKeySetData() { mProperSigningKeySet = KEYSET_UNASSIGNED; @@ -132,7 +131,7 @@ public class PackageKeySetData { return mDefinedKeySets; } - protected Map<String, Long> getAliases() { + protected ArrayMap<String, Long> getAliases() { return mKeySetAliases; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b79e157..f9a85df 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -84,6 +84,8 @@ import android.app.AppGlobals; import android.app.IActivityManager; import android.app.admin.IDevicePolicyManager; import android.app.backup.IBackupManager; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -199,8 +201,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -329,9 +329,11 @@ public class PackageManagerService extends IPackageManager.Stub { final boolean mFactoryTest; final boolean mOnlyCore; final boolean mLazyDexOpt; + final long mDexOptLRUThresholdInMills; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; + final boolean mIsUpgrade; // This is where all application persistent data goes. final File mAppDataDir; @@ -371,20 +373,20 @@ public class PackageManagerService extends IPackageManager.Stub { // Keys are String (package name), values are Package. This also serves // as the lock for the global state. Methods that must be called with // this lock held have the prefix "LP". - final HashMap<String, PackageParser.Package> mPackages = - new HashMap<String, PackageParser.Package>(); + final ArrayMap<String, PackageParser.Package> mPackages = + new ArrayMap<String, PackageParser.Package>(); // Tracks available target package names -> overlay package paths. - final HashMap<String, HashMap<String, PackageParser.Package>> mOverlays = - new HashMap<String, HashMap<String, PackageParser.Package>>(); + final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays = + new ArrayMap<String, ArrayMap<String, PackageParser.Package>>(); final Settings mSettings; boolean mRestoredSettings; // System configuration read by SystemConfig. final int[] mGlobalGids; - final SparseArray<HashSet<String>> mSystemPermissions; - final HashMap<String, FeatureInfo> mAvailableFeatures; + final SparseArray<ArraySet<String>> mSystemPermissions; + final ArrayMap<String, FeatureInfo> mAvailableFeatures; // If mac_permissions.xml was found for seinfo labeling. boolean mFoundPolicyFile; @@ -403,8 +405,8 @@ public class PackageManagerService extends IPackageManager.Stub { } // Currently known shared libraries. - final HashMap<String, SharedLibraryEntry> mSharedLibraries = - new HashMap<String, SharedLibraryEntry>(); + final ArrayMap<String, SharedLibraryEntry> mSharedLibraries = + new ArrayMap<String, SharedLibraryEntry>(); // All available activities, for your resolving pleasure. final ActivityIntentResolver mActivities = @@ -422,23 +424,23 @@ public class PackageManagerService extends IPackageManager.Stub { // Mapping from provider base names (first directory in content URI codePath) // to the provider information. - final HashMap<String, PackageParser.Provider> mProvidersByAuthority = - new HashMap<String, PackageParser.Provider>(); + final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority = + new ArrayMap<String, PackageParser.Provider>(); // Mapping from instrumentation class names to info about them. - final HashMap<ComponentName, PackageParser.Instrumentation> mInstrumentation = - new HashMap<ComponentName, PackageParser.Instrumentation>(); + final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation = + new ArrayMap<ComponentName, PackageParser.Instrumentation>(); // Mapping from permission names to info about them. - final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups = - new HashMap<String, PackageParser.PermissionGroup>(); + final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups = + new ArrayMap<String, PackageParser.PermissionGroup>(); // Packages whose data we have transfered into another package, thus // should no longer exist. - final HashSet<String> mTransferedPackages = new HashSet<String>(); + final ArraySet<String> mTransferedPackages = new ArraySet<String>(); // Broadcast actions that are only available to the system. - final HashSet<String> mProtectedBroadcasts = new HashSet<String>(); + final ArraySet<String> mProtectedBroadcasts = new ArraySet<String>(); /** List of packages waiting for verification. */ final SparseArray<PackageVerificationState> mPendingVerification @@ -449,7 +451,7 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageInstallerService mInstallerService; - HashSet<PackageParser.Package> mDeferredDexOpt = null; + ArraySet<PackageParser.Package> mDeferredDexOpt = null; // Cache of users who need badging. SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray(); @@ -473,24 +475,24 @@ public class PackageManagerService extends IPackageManager.Stub { // Set of pending broadcasts for aggregating enable/disable of components. static class PendingPackageBroadcasts { // for each user id, a map of <package name -> components within that package> - final SparseArray<HashMap<String, ArrayList<String>>> mUidMap; + final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap; public PendingPackageBroadcasts() { - mUidMap = new SparseArray<HashMap<String, ArrayList<String>>>(2); + mUidMap = new SparseArray<ArrayMap<String, ArrayList<String>>>(2); } public ArrayList<String> get(int userId, String packageName) { - HashMap<String, ArrayList<String>> packages = getOrAllocate(userId); + ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId); return packages.get(packageName); } public void put(int userId, String packageName, ArrayList<String> components) { - HashMap<String, ArrayList<String>> packages = getOrAllocate(userId); + ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId); packages.put(packageName, components); } public void remove(int userId, String packageName) { - HashMap<String, ArrayList<String>> packages = mUidMap.get(userId); + ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId); if (packages != null) { packages.remove(packageName); } @@ -508,7 +510,7 @@ public class PackageManagerService extends IPackageManager.Stub { return mUidMap.keyAt(n); } - public HashMap<String, ArrayList<String>> packagesForUserId(int userId) { + public ArrayMap<String, ArrayList<String>> packagesForUserId(int userId) { return mUidMap.get(userId); } @@ -525,10 +527,10 @@ public class PackageManagerService extends IPackageManager.Stub { mUidMap.clear(); } - private HashMap<String, ArrayList<String>> getOrAllocate(int userId) { - HashMap<String, ArrayList<String>> map = mUidMap.get(userId); + private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) { + ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId); if (map == null) { - map = new HashMap<String, ArrayList<String>>(); + map = new ArrayMap<String, ArrayList<String>>(); mUidMap.put(userId, map); } return map; @@ -565,7 +567,7 @@ public class PackageManagerService extends IPackageManager.Stub { static UserManagerService sUserManager; // Stores a list of users whose package restrictions file needs to be updated - private HashSet<Integer> mDirtyUsers = new HashSet<Integer>(); + private ArraySet<Integer> mDirtyUsers = new ArraySet<Integer>(); final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); @@ -1294,6 +1296,15 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); + // TODO: add a property to control this? + long dexOptLRUThresholdInMinutes; + if (mLazyDexOpt) { + dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. + } else { + dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. + } + mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; + String separateProcesses = SystemProperties.get("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { @@ -1384,7 +1395,7 @@ public class PackageManagerService extends IPackageManager.Stub { // scanning install directories. final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING; - final HashSet<String> alreadyDexOpted = new HashSet<String>(); + final ArraySet<String> alreadyDexOpted = new ArraySet<String>(); /** * Add everything in the in the boot class path to the @@ -1750,7 +1761,8 @@ public class PackageManagerService extends IPackageManager.Stub { // If this is first boot after an OTA, and a normal boot, then // we need to clear code cache directories. - if (!Build.FINGERPRINT.equals(mSettings.mFingerprint) && !onlyCore) { + mIsUpgrade = !Build.FINGERPRINT.equals(mSettings.mFingerprint); + if (mIsUpgrade && !onlyCore) { Slog.i(TAG, "Build fingerprint changed; clearing code caches"); for (String pkgName : mSettings.mPackages.keySet()) { deleteCodeCacheDirsLI(pkgName); @@ -1790,6 +1802,11 @@ public class PackageManagerService extends IPackageManager.Stub { return mOnlyCore; } + @Override + public boolean isUpgrade() { + return mIsUpgrade; + } + private String getRequiredVerifierLPr() { final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE, @@ -2369,7 +2386,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.PERMISSION_GRANTED; } } else { - HashSet<String> perms = mSystemPermissions.get(uid); + ArraySet<String> perms = mSystemPermissions.get(uid); if (perms != null && perms.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } @@ -2790,11 +2807,11 @@ public class PackageManagerService extends IPackageManager.Stub { PackageManager.SIGNATURE_NO_MATCH; } - HashSet<Signature> set1 = new HashSet<Signature>(); + ArraySet<Signature> set1 = new ArraySet<Signature>(); for (Signature sig : s1) { set1.add(sig); } - HashSet<Signature> set2 = new HashSet<Signature>(); + ArraySet<Signature> set2 = new ArraySet<Signature>(); for (Signature sig : s2) { set2.add(sig); } @@ -2829,11 +2846,11 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.SIGNATURE_NO_MATCH; } - HashSet<Signature> existingSet = new HashSet<Signature>(); + ArraySet<Signature> existingSet = new ArraySet<Signature>(); for (Signature sig : existingSigs.mSignatures) { existingSet.add(sig); } - HashSet<Signature> scannedCompatSet = new HashSet<Signature>(); + ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>(); for (Signature sig : scannedPkg.mSignatures) { try { Signature[] chainSignatures = sig.getChainSignatures(); @@ -3249,7 +3266,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (DEBUG_PREFERRED) { Slog.v(TAG, "Preferred activity bookkeeping changed; writing restrictions"); } - mSettings.writePackageRestrictionsLPr(userId); + scheduleWritePackageRestrictionsLocked(userId); } } } @@ -4023,7 +4040,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void createIdmapsForPackageLI(PackageParser.Package pkg) { - HashMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName); + ArrayMap<String, PackageParser.Package> overlays = mOverlays.get(pkg.packageName); if (overlays == null) { Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages"); return; @@ -4044,7 +4061,7 @@ public class PackageManagerService extends IPackageManager.Stub { opkg.baseCodePath + ": overlay not trusted"); return false; } - HashMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName); + ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName); if (overlaySet == null) { Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " + opkg.baseCodePath + " but target package has no known overlays"); @@ -4465,7 +4482,7 @@ public class PackageManagerService extends IPackageManager.Stub { public void performBootDexOpt() { enforceSystemOrRoot("Only the system can request dexopt be performed"); - final HashSet<PackageParser.Package> pkgs; + final ArraySet<PackageParser.Package> pkgs; synchronized (mPackages) { pkgs = mDeferredDexOpt; mDeferredDexOpt = null; @@ -4488,7 +4505,7 @@ public class PackageManagerService extends IPackageManager.Stub { } // Give priority to system apps that listen for pre boot complete. Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); - HashSet<String> pkgNames = getPackageNamesForIntent(intent); + ArraySet<String> pkgNames = getPackageNamesForIntent(intent); for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { PackageParser.Package pkg = it.next(); if (pkgNames.contains(pkg.packageName)) { @@ -4562,28 +4579,19 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private void filterRecentlyUsedApps(HashSet<PackageParser.Package> pkgs) { + private void filterRecentlyUsedApps(ArraySet<PackageParser.Package> pkgs) { // Filter out packages that aren't recently used. // // The exception is first boot of a non-eng device (aka !mLazyDexOpt), which // should do a full dexopt. if (mLazyDexOpt || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) { - // TODO: add a property to control this? - long dexOptLRUThresholdInMinutes; - if (mLazyDexOpt) { - dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds. - } else { - dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users. - } - long dexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000; - int total = pkgs.size(); int skipped = 0; long now = System.currentTimeMillis(); for (Iterator<PackageParser.Package> i = pkgs.iterator(); i.hasNext();) { PackageParser.Package pkg = i.next(); long then = pkg.mLastPackageUsageTimeInMills; - if (then + dexOptLRUThresholdInMills < now) { + if (then + mDexOptLRUThresholdInMills < now) { if (DEBUG_DEXOPT) { Log.i(TAG, "Skipping dexopt of " + pkg.packageName + " last resumed: " + ((then == 0) ? "never" : new Date(then))); @@ -4598,14 +4606,14 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private HashSet<String> getPackageNamesForIntent(Intent intent) { + private ArraySet<String> getPackageNamesForIntent(Intent intent) { List<ResolveInfo> ris = null; try { ris = AppGlobals.getPackageManager().queryIntentReceivers( intent, null, 0, UserHandle.USER_OWNER); } catch (RemoteException e) { } - HashSet<String> pkgNames = new HashSet<String>(); + ArraySet<String> pkgNames = new ArraySet<String>(); if (ris != null) { for (ResolveInfo ri : ris) { pkgNames.add(ri.activityInfo.packageName); @@ -4683,8 +4691,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } - public HashSet<String> getPackagesThatNeedDexOpt() { - HashSet<String> pkgs = null; + public ArraySet<String> getPackagesThatNeedDexOpt() { + ArraySet<String> pkgs = null; synchronized (mPackages) { for (PackageParser.Package p : mPackages.values()) { if (DEBUG_DEXOPT) { @@ -4694,7 +4702,7 @@ public class PackageManagerService extends IPackageManager.Stub { continue; } if (pkgs == null) { - pkgs = new HashSet<String>(); + pkgs = new ArraySet<String>(); } pkgs.add(p.packageName); } @@ -4707,7 +4715,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private void performDexOptLibsLI(ArrayList<String> libs, String[] instructionSets, - boolean forceDex, boolean defer, HashSet<String> done) { + boolean forceDex, boolean defer, ArraySet<String> done) { for (int i=0; i<libs.size(); i++) { PackageParser.Package libPkg; String libName; @@ -4732,7 +4740,7 @@ public class PackageManagerService extends IPackageManager.Stub { static final int DEX_OPT_FAILED = -1; private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets, - boolean forceDex, boolean defer, HashSet<String> done) { + boolean forceDex, boolean defer, ArraySet<String> done) { final String[] instructionSets = targetInstructionSets != null ? targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo); @@ -4810,7 +4818,7 @@ public class PackageManagerService extends IPackageManager.Stub { // our list of deferred dexopts. if (defer && isDexOptNeeded != DexFile.UP_TO_DATE) { if (mDeferredDexOpt == null) { - mDeferredDexOpt = new HashSet<PackageParser.Package>(); + mDeferredDexOpt = new ArraySet<PackageParser.Package>(); } mDeferredDexOpt.add(pkg); return DEX_OPT_DEFERRED; @@ -4907,7 +4915,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private static String[] getDexCodeInstructionSets(String[] instructionSets) { - HashSet<String> dexCodeInstructionSets = new HashSet<String>(instructionSets.length); + ArraySet<String> dexCodeInstructionSets = new ArraySet<String>(instructionSets.length); for (String instructionSet : instructionSets) { dexCodeInstructionSets.add(getDexCodeInstructionSet(instructionSet)); } @@ -4950,9 +4958,9 @@ public class PackageManagerService extends IPackageManager.Stub { private int performDexOptLI(PackageParser.Package pkg, String[] instructionSets, boolean forceDex, boolean defer, boolean inclDependencies) { - HashSet<String> done; + ArraySet<String> done; if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) { - done = new HashSet<String>(); + done = new ArraySet<String>(); done.add(pkg.packageName); } else { done = null; @@ -6137,7 +6145,7 @@ public class PackageManagerService extends IPackageManager.Stub { r = null; for (i=0; i<N; i++) { PackageParser.Permission p = pkg.permissions.get(i); - HashMap<String, BasePermission> permissionMap = + ArrayMap<String, BasePermission> permissionMap = p.tree ? mSettings.mPermissionTrees : mSettings.mPermissions; p.group = mPermissionGroups.get(p.info.group); @@ -6265,9 +6273,9 @@ public class PackageManagerService extends IPackageManager.Stub { if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) { if (!mOverlays.containsKey(pkg.mOverlayTarget)) { mOverlays.put(pkg.mOverlayTarget, - new HashMap<String, PackageParser.Package>()); + new ArrayMap<String, PackageParser.Package>()); } - HashMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget); + ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget); map.put(pkg.packageName, pkg); PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget); if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) { @@ -6928,13 +6936,13 @@ public class PackageManagerService extends IPackageManager.Stub { return; } final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - HashSet<String> origPermissions = gp.grantedPermissions; + ArraySet<String> origPermissions = gp.grantedPermissions; boolean changedPermission = false; if (replace) { ps.permissionsFixed = false; if (gp == ps) { - origPermissions = new HashSet<String>(gp.grantedPermissions); + origPermissions = new ArraySet<String>(gp.grantedPermissions); gp.grantedPermissions.clear(); gp.gids = mGlobalGids; } @@ -7081,7 +7089,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private boolean grantSignaturePermission(String perm, PackageParser.Package pkg, - BasePermission bp, HashSet<String> origPermissions) { + BasePermission bp, ArraySet<String> origPermissions) { boolean allowed; allowed = (compareSignatures( bp.packageSetting.signatures.mSignatures, pkg.mSignatures) @@ -7339,8 +7347,8 @@ public class PackageManagerService extends IPackageManager.Stub { // } // Keys are String (activity class name), values are Activity. - private final HashMap<ComponentName, PackageParser.Activity> mActivities - = new HashMap<ComponentName, PackageParser.Activity>(); + private final ArrayMap<ComponentName, PackageParser.Activity> mActivities + = new ArrayMap<ComponentName, PackageParser.Activity>(); private int mFlags; } @@ -7538,8 +7546,8 @@ public class PackageManagerService extends IPackageManager.Stub { // } // Keys are String (activity class name), values are Activity. - private final HashMap<ComponentName, PackageParser.Service> mServices - = new HashMap<ComponentName, PackageParser.Service>(); + private final ArrayMap<ComponentName, PackageParser.Service> mServices + = new ArrayMap<ComponentName, PackageParser.Service>(); private int mFlags; }; @@ -7732,8 +7740,8 @@ public class PackageManagerService extends IPackageManager.Stub { out.println(Integer.toHexString(System.identityHashCode(filter))); } - private final HashMap<ComponentName, PackageParser.Provider> mProviders - = new HashMap<ComponentName, PackageParser.Provider>(); + private final ArrayMap<ComponentName, PackageParser.Provider> mProviders + = new ArrayMap<ComponentName, PackageParser.Provider>(); private int mFlags; }; @@ -10402,6 +10410,60 @@ public class PackageManagerService extends IPackageManager.Stub { String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { + // Check if installing already existing package + if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + String oldName = mSettings.mRenamedPackages.get(pkgName); + if (pkg.mOriginalPackages != null + && pkg.mOriginalPackages.contains(oldName) + && mPackages.containsKey(oldName)) { + // This package is derived from an original package, + // and this device has been updating from that original + // name. We must continue using the original name, so + // rename the new package here. + pkg.setPackageName(oldName); + pkgName = pkg.packageName; + replace = true; + if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" + + oldName + " pkgName=" + pkgName); + } else if (mPackages.containsKey(pkgName)) { + // This package, under its official name, already exists + // on the device; we should replace it. + replace = true; + if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName); + } + } + + PackageSetting ps = mSettings.mPackages.get(pkgName); + if (ps != null) { + if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); + + // Quick sanity check that we're signed correctly if updating; + // we'll check this again later when scanning, but we want to + // bail early here before tripping over redefined permissions. + if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) { + try { + verifySignaturesLP(ps, pkg); + } catch (PackageManagerException e) { + res.setError(e.error, e.getMessage()); + return; + } + } else { + if (!checkUpgradeKeySetLP(ps, pkg)) { + res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + + pkg.packageName + " upgrade keys do not match the " + + "previously installed version"); + return; + } + } + + oldCodePath = mSettings.mPackages.get(pkgName).codePathString; + if (ps.pkg != null && ps.pkg.applicationInfo != null) { + systemApp = (ps.pkg.applicationInfo.flags & + ApplicationInfo.FLAG_SYSTEM) != 0; + } + res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); + } + // Check whether the newly-scanned package wants to define an already-defined perm int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { @@ -10442,38 +10504,6 @@ public class PackageManagerService extends IPackageManager.Stub { } } - // Check if installing already existing package - if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { - String oldName = mSettings.mRenamedPackages.get(pkgName); - if (pkg.mOriginalPackages != null - && pkg.mOriginalPackages.contains(oldName) - && mPackages.containsKey(oldName)) { - // This package is derived from an original package, - // and this device has been updating from that original - // name. We must continue using the original name, so - // rename the new package here. - pkg.setPackageName(oldName); - pkgName = pkg.packageName; - replace = true; - if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" - + oldName + " pkgName=" + pkgName); - } else if (mPackages.containsKey(pkgName)) { - // This package, under its official name, already exists - // on the device; we should replace it. - replace = true; - if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName); - } - } - PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps); - oldCodePath = mSettings.mPackages.get(pkgName).codePathString; - if (ps.pkg != null && ps.pkg.applicationInfo != null) { - systemApp = (ps.pkg.applicationInfo.flags & - ApplicationInfo.FLAG_SYSTEM) != 0; - } - res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); - } } if (systemApp && onSd) { @@ -11536,7 +11566,7 @@ public class PackageManagerService extends IPackageManager.Stub { + userId + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); - mSettings.writePackageRestrictionsLPr(userId); + scheduleWritePackageRestrictionsLocked(userId); } } @@ -11652,8 +11682,7 @@ public class PackageManagerService extends IPackageManager.Stub { int user = UserHandle.getCallingUserId(); if (clearPackagePreferredActivitiesLPw(packageName, user)) { - mSettings.writePackageRestrictionsLPr(user); - scheduleWriteSettingsLocked(); + scheduleWritePackageRestrictionsLocked(user); } } } @@ -11703,8 +11732,7 @@ public class PackageManagerService extends IPackageManager.Stub { int user = UserHandle.getCallingUserId(); clearPackagePreferredActivitiesLPw(null, user); mSettings.readDefaultPreferredAppsLPw(this, user); - mSettings.writePackageRestrictionsLPr(user); - scheduleWriteSettingsLocked(); + scheduleWritePackageRestrictionsLocked(user); } } @@ -11756,7 +11784,7 @@ public class PackageManagerService extends IPackageManager.Stub { filter.dump(new LogPrinter(Log.INFO, TAG), " "); mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( new PersistentPreferredActivity(filter, activity)); - mSettings.writePackageRestrictionsLPr(userId); + scheduleWritePackageRestrictionsLocked(userId); } } @@ -11798,7 +11826,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if (changed) { - mSettings.writePackageRestrictionsLPr(userId); + scheduleWritePackageRestrictionsLocked(userId); } } } @@ -11819,7 +11847,7 @@ public class PackageManagerService extends IPackageManager.Stub { CrossProfileIntentFilter filter = new CrossProfileIntentFilter(intentFilter, ownerPackage, UserHandle.getUserId(callingUid), targetUserId, flags); mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(filter); - mSettings.writePackageRestrictionsLPr(sourceUserId); + scheduleWritePackageRestrictionsLocked(sourceUserId); } } @@ -11835,15 +11863,15 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); - HashSet<CrossProfileIntentFilter> set = - new HashSet<CrossProfileIntentFilter>(resolver.filterSet()); + ArraySet<CrossProfileIntentFilter> set = + new ArraySet<CrossProfileIntentFilter>(resolver.filterSet()); for (CrossProfileIntentFilter filter : set) { if (filter.getOwnerPackage().equals(ownerPackage) && filter.getOwnerUserId() == callingUserId) { resolver.removeFilter(filter); } } - mSettings.writePackageRestrictionsLPr(sourceUserId); + scheduleWritePackageRestrictionsLocked(sourceUserId); } } @@ -13381,4 +13409,25 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } } + + public void getUsageStatsIfNoPackageUsageInfo() { + if (!mPackageUsage.isHistoricalPackageUsageAvailable()) { + UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE); + if (usm == null) { + throw new IllegalStateException("UsageStatsManager must be initialized"); + } + long now = System.currentTimeMillis(); + Map<String, UsageStats> stats = usm.queryAndAggregateUsageStats(now - mDexOptLRUThresholdInMills, now); + for (Map.Entry<String, UsageStats> entry : stats.entrySet()) { + String packageName = entry.getKey(); + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + continue; + } + UsageStats usage = entry.getValue(); + pkg.mLastPackageUsageTimeInMills = usage.getLastTimeUsed(); + mPackageUsage.mIsHistoricalPackageUsageAvailable = true; + } + } + } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index bf13fd9..1dcadb4 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -21,10 +21,10 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import android.content.pm.PackageUserState; +import android.util.ArraySet; import android.util.SparseArray; import java.io.File; -import java.util.HashSet; /** * Settings base class for pending and resolved classes. @@ -321,8 +321,8 @@ class PackageSettingBase extends GrantedPermissions { void setUserState(int userId, int enabled, boolean installed, boolean stopped, boolean notLaunched, boolean hidden, - String lastDisableAppCaller, HashSet<String> enabledComponents, - HashSet<String> disabledComponents, boolean blockUninstall) { + String lastDisableAppCaller, ArraySet<String> enabledComponents, + ArraySet<String> disabledComponents, boolean blockUninstall) { PackageUserState state = modifyUserState(userId); state.enabled = enabled; state.installed = installed; @@ -335,39 +335,39 @@ class PackageSettingBase extends GrantedPermissions { state.blockUninstall = blockUninstall; } - HashSet<String> getEnabledComponents(int userId) { + ArraySet<String> getEnabledComponents(int userId) { return readUserState(userId).enabledComponents; } - HashSet<String> getDisabledComponents(int userId) { + ArraySet<String> getDisabledComponents(int userId) { return readUserState(userId).disabledComponents; } - void setEnabledComponents(HashSet<String> components, int userId) { + void setEnabledComponents(ArraySet<String> components, int userId) { modifyUserState(userId).enabledComponents = components; } - void setDisabledComponents(HashSet<String> components, int userId) { + void setDisabledComponents(ArraySet<String> components, int userId) { modifyUserState(userId).disabledComponents = components; } - void setEnabledComponentsCopy(HashSet<String> components, int userId) { + void setEnabledComponentsCopy(ArraySet<String> components, int userId) { modifyUserState(userId).enabledComponents = components != null - ? new HashSet<String>(components) : null; + ? new ArraySet<String>(components) : null; } - void setDisabledComponentsCopy(HashSet<String> components, int userId) { + void setDisabledComponentsCopy(ArraySet<String> components, int userId) { modifyUserState(userId).disabledComponents = components != null - ? new HashSet<String>(components) : null; + ? new ArraySet<String>(components) : null; } PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) { PackageUserState state = modifyUserState(userId); if (disabled && state.disabledComponents == null) { - state.disabledComponents = new HashSet<String>(1); + state.disabledComponents = new ArraySet<String>(1); } if (enabled && state.enabledComponents == null) { - state.enabledComponents = new HashSet<String>(1); + state.enabledComponents = new ArraySet<String>(1); } return state; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7de56c8..200eb5f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -63,6 +63,8 @@ import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -78,8 +80,6 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -159,11 +159,11 @@ final class Settings { private final File mStoppedPackagesFilename; private final File mBackupStoppedPackagesFilename; - final HashMap<String, PackageSetting> mPackages = - new HashMap<String, PackageSetting>(); + final ArrayMap<String, PackageSetting> mPackages = + new ArrayMap<String, PackageSetting>(); // List of replaced system applications - private final HashMap<String, PackageSetting> mDisabledSysPackages = - new HashMap<String, PackageSetting>(); + private final ArrayMap<String, PackageSetting> mDisabledSysPackages = + new ArrayMap<String, PackageSetting>(); private static int mFirstAvailableUid = 0; @@ -206,8 +206,8 @@ final class Settings { final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers = new SparseArray<CrossProfileIntentResolver>(); - final HashMap<String, SharedUserSetting> mSharedUsers = - new HashMap<String, SharedUserSetting>(); + final ArrayMap<String, SharedUserSetting> mSharedUsers = + new ArrayMap<String, SharedUserSetting>(); private final ArrayList<Object> mUserIds = new ArrayList<Object>(); private final SparseArray<Object> mOtherUserIds = new SparseArray<Object>(); @@ -217,12 +217,12 @@ final class Settings { new ArrayList<Signature>(); // Mapping from permission names to info about them. - final HashMap<String, BasePermission> mPermissions = - new HashMap<String, BasePermission>(); + final ArrayMap<String, BasePermission> mPermissions = + new ArrayMap<String, BasePermission>(); // Mapping from permission tree names to info about them. - final HashMap<String, BasePermission> mPermissionTrees = - new HashMap<String, BasePermission>(); + final ArrayMap<String, BasePermission> mPermissionTrees = + new ArrayMap<String, BasePermission>(); // Packages that have been uninstalled and still need their external // storage data deleted. @@ -232,7 +232,7 @@ final class Settings { // Keys are the new names of the packages, values are the original // names. The packages appear everwhere else under their original // names. - final HashMap<String, String> mRenamedPackages = new HashMap<String, String>(); + final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>(); final StringBuilder mReadMessages = new StringBuilder(); @@ -437,7 +437,7 @@ final class Settings { void transferPermissionsLPw(String origPkg, String newPkg) { // Transfer ownership of permissions to the new package. for (int i=0; i<2; i++) { - HashMap<String, BasePermission> permissions = + ArrayMap<String, BasePermission> permissions = i == 0 ? mPermissionTrees : mPermissions; for (BasePermission bp : permissions.values()) { if (origPkg.equals(bp.sourcePackage)) { @@ -582,7 +582,7 @@ final class Settings { } p.appId = dis.appId; // Clone permissions - p.grantedPermissions = new HashSet<String>(dis.grantedPermissions); + p.grantedPermissions = new ArraySet<String>(dis.grantedPermissions); // Clone component info List<UserInfo> users = getAllUsers(); if (users != null) { @@ -1138,8 +1138,8 @@ final class Settings { final boolean blockUninstall = blockUninstallStr == null ? false : Boolean.parseBoolean(blockUninstallStr); - HashSet<String> enabledComponents = null; - HashSet<String> disabledComponents = null; + ArraySet<String> enabledComponents = null; + ArraySet<String> disabledComponents = null; int packageDepth = parser.getDepth(); while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -1189,9 +1189,9 @@ final class Settings { } } - private HashSet<String> readComponentsLPr(XmlPullParser parser) + private ArraySet<String> readComponentsLPr(XmlPullParser parser) throws IOException, XmlPullParserException { - HashSet<String> components = null; + ArraySet<String> components = null; int type; int outerDepth = parser.getDepth(); String tagName; @@ -1207,7 +1207,7 @@ final class Settings { String componentName = parser.getAttributeValue(null, ATTR_NAME); if (componentName != null) { if (components == null) { - components = new HashSet<String>(); + components = new ArraySet<String>(); } components.add(componentName); } @@ -1921,7 +1921,7 @@ final class Settings { } ArrayList<PackageSetting> getListOfIncompleteInstallPackagesLPr() { - final HashSet<String> kList = new HashSet<String>(mPackages.keySet()); + final ArraySet<String> kList = new ArraySet<String>(mPackages.keySet()); final Iterator<String> its = kList.iterator(); final ArrayList<PackageSetting> ret = new ArrayList<PackageSetting>(); while (its.hasNext()) { @@ -2511,7 +2511,7 @@ final class Settings { return defValue; } - private void readPermissionsLPw(HashMap<String, BasePermission> out, XmlPullParser parser) + private void readPermissionsLPw(ArrayMap<String, BasePermission> out, XmlPullParser parser) throws IOException, XmlPullParserException { int outerDepth = parser.getDepth(); int type; @@ -3016,7 +3016,7 @@ final class Settings { } } - private void readGrantedPermissionsLPw(XmlPullParser parser, HashSet<String> outPerms) + private void readGrantedPermissionsLPw(XmlPullParser parser, ArraySet<String> outPerms) throws IOException, XmlPullParserException { int outerDepth = parser.getDepth(); int type; @@ -3090,8 +3090,8 @@ final class Settings { int sourceUserId = mCrossProfileIntentResolvers.keyAt(i); CrossProfileIntentResolver cpir = mCrossProfileIntentResolvers.get(sourceUserId); boolean needsWriting = false; - HashSet<CrossProfileIntentFilter> cpifs = - new HashSet<CrossProfileIntentFilter>(cpir.filterSet()); + ArraySet<CrossProfileIntentFilter> cpifs = + new ArraySet<CrossProfileIntentFilter>(cpir.filterSet()); for (CrossProfileIntentFilter cpif : cpifs) { if (cpif.getTargetUserId() == userId) { needsWriting = true; @@ -3147,7 +3147,7 @@ final class Settings { return ps; } - private String compToString(HashSet<String> cmp) { + private String compToString(ArraySet<String> cmp) { return cmp != null ? Arrays.toString(cmp.toArray()) : "[]"; } @@ -3477,7 +3477,7 @@ final class Settings { pw.print(prefix); pw.print(" lastDisabledCaller: "); pw.println(lastDisabledAppCaller); } - HashSet<String> cmp = ps.getDisabledComponents(user.id); + ArraySet<String> cmp = ps.getDisabledComponents(user.id); if (cmp != null && cmp.size() > 0) { pw.print(prefix); pw.println(" disabledComponents:"); for (String s : cmp) { diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index ca1eeea..2b406f7 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -16,7 +16,7 @@ package com.android.server.pm; -import java.util.HashSet; +import android.util.ArraySet; /** * Settings data for a particular shared user ID we know about. @@ -29,7 +29,7 @@ final class SharedUserSetting extends GrantedPermissions { // flags that are associated with this uid, regardless of any package flags int uidFlags; - final HashSet<PackageSetting> packages = new HashSet<PackageSetting>(); + final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>(); final PackageSignatures signatures = new PackageSignatures(); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index f85e2d9..15e0bf0 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -26,6 +26,7 @@ import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.util.Slog; +import android.view.WindowManager; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; @@ -295,9 +296,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } - /** + /** * Hide or show the on-screen Menu key. Only call this from the window manager, typically in - * response to a window with FLAG_NEEDS_MENU_KEY set. + * response to a window with {@link android.view.WindowManager.LayoutParams#needsMenuKey} set + * to {@link android.view.WindowManager.LayoutParams#NEEDS_MENU_SET_TRUE}. */ @Override public void topAppWindowChanged(final boolean menuVisible) { @@ -523,6 +525,20 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } @Override + public void onNotificationActionClick(String key, int actionIndex) { + enforceStatusBarService(); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationActionClick(callingUid, callingPid, key, + actionIndex); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId) { enforceStatusBarService(); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index f9b1704..65cb35b 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -16,6 +16,7 @@ package com.android.server.trust; +import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; @@ -24,6 +25,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.Manifest; +import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; import android.app.trust.ITrustListener; @@ -41,6 +43,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; +import android.os.Binder; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; @@ -100,8 +103,10 @@ public class TrustManagerService extends SystemService { /* package */ final TrustArchive mArchive = new TrustArchive(); private final Context mContext; private final LockPatternUtils mLockPatternUtils; + private final UserManager mUserManager; - private UserManager mUserManager; + @GuardedBy("mUserIsTrusted") + private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray(); public TrustManagerService(Context context) { super(context); @@ -117,11 +122,15 @@ public class TrustManagerService extends SystemService { @Override public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) { + if (isSafeMode()) { + // No trust agents in safe mode. + return; + } + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); mReceiver.register(mContext); refreshAgentList(UserHandle.USER_ALL); - } else if (phase == SystemService.PHASE_BOOT_COMPLETED && !isSafeMode()) { + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { maybeEnableFactoryTrustAgents(mLockPatternUtils, UserHandle.USER_OWNER); } } @@ -160,11 +169,19 @@ public class TrustManagerService extends SystemService { public void updateTrust(int userId, boolean initiatedByUser) { dispatchOnTrustManagedChanged(aggregateIsTrustManaged(userId), userId); - dispatchOnTrustChanged(aggregateIsTrusted(userId), userId, initiatedByUser); + boolean trusted = aggregateIsTrusted(userId); + synchronized (mUserIsTrusted) { + mUserIsTrusted.put(userId, trusted); + } + dispatchOnTrustChanged(trusted, userId, initiatedByUser); } void refreshAgentList(int userId) { if (DEBUG) Slog.d(TAG, "refreshAgentList()"); + if (isSafeMode()) { + // Don't ever bind to trust agents in safe mode. + return; + } if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_OWNER) { Log.e(TAG, "refreshAgentList(userId=" + userId + "): Invalid user handle," + " must be USER_ALL or a specific user.", new Throwable("here")); @@ -547,6 +564,16 @@ public class TrustManagerService extends SystemService { mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget(); } + @Override + public boolean isTrusted(int userId) throws RemoteException { + userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, + false /* allowAll */, true /* requireFull */, "isTrusted", null); + userId = resolveProfileParent(userId); + synchronized (mUserIsTrusted) { + return mUserIsTrusted.get(userId); + } + } + private void enforceReportPermission() { mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events"); @@ -561,6 +588,10 @@ public class TrustManagerService extends SystemService { protected void dump(FileDescriptor fd, final PrintWriter fout, String[] args) { mContext.enforceCallingPermission(Manifest.permission.DUMP, "dumping TrustManagerService"); + if (isSafeMode()) { + fout.println("disabled because the system is in safe mode."); + return; + } final UserInfo currentUser; final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); try { @@ -623,6 +654,19 @@ public class TrustManagerService extends SystemService { } }; + private int resolveProfileParent(int userId) { + long identity = Binder.clearCallingIdentity(); + try { + UserInfo parent = mUserManager.getProfileParent(userId); + if (parent != null) { + return parent.getUserHandle().getIdentifier(); + } + return userId; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 558ffb5..c12dd63 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -55,7 +55,7 @@ final class TvInputHal implements Handler.Callback { private native long nativeOpen(); - private static native int nativeAddStream(long ptr, int deviceId, int streamId, + private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId, Surface surface); private static native int nativeRemoveStream(long ptr, int deviceId, int streamId); private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId, @@ -80,7 +80,7 @@ final class TvInputHal implements Handler.Callback { } } - public int addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) { + public int addOrUpdateStream(int deviceId, Surface surface, TvStreamConfig streamConfig) { synchronized (mLock) { if (mPtr == 0) { return ERROR_NO_INIT; @@ -89,7 +89,7 @@ final class TvInputHal implements Handler.Callback { if (generation != streamConfig.getGeneration()) { return ERROR_STALE_CONFIG; } - if (nativeAddStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) { + if (nativeAddOrUpdateStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) { return SUCCESS; } else { return ERROR_UNKNOWN; diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 77ab33b..85659cf 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -122,6 +122,8 @@ class TvInputHardwareManager implements TvInputHal.Callback { } catch (RemoteException e) { Slog.w(TAG, "Error registering listeners to HdmiControlService:", e); } + } else { + Slog.w(TAG, "HdmiControlService is not available"); } } } @@ -186,6 +188,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { return; } connection.updateConfigsLocked(configs); + String inputId = mHardwareInputIdMap.get(deviceId); + if (inputId != null) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget(); + } try { connection.getCallbackLocked().onStreamConfigChanged(configs); } catch (RemoteException e) { @@ -255,6 +262,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { mHardwareInputIdMap.put(deviceId, info.getId()); mInputMap.put(info.getId(), info); + // Process pending state changes + + // For logical HDMI devices, they have information from HDMI CEC signals. for (int i = 0; i < mHdmiStateMap.size(); ++i) { TvInputHardwareInfo hardwareInfo = findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i)); @@ -266,8 +276,17 @@ class TvInputHardwareManager implements TvInputHal.Callback { mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, convertConnectedToState(mHdmiStateMap.valueAt(i)), 0, inputId).sendToTarget(); + return; } } + // For the rest of the devices, we can tell by the number of available streams. + Connection connection = mConnections.get(deviceId); + if (connection != null) { + mHandler.obtainMessage(ListenerHandler.STATE_CHANGED, + convertConnectedToState(connection.getConfigsLocked().length > 0), 0, + info.getId()).sendToTarget(); + return; + } } } @@ -666,14 +685,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); mActiveConfig = null; } else { - if (config != mActiveConfig && mActiveConfig != null) { + if (!config.equals(mActiveConfig)) { result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig); if (result != TvInputHal.SUCCESS) { mActiveConfig = null; return false; } } - result = mHal.addStream(mInfo.getDeviceId(), surface, config); + result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); if (result == TvInputHal.SUCCESS) { mActiveConfig = config; } @@ -743,7 +762,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { && sinkConfig.channelMask() != mDesiredChannelMask) || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT && sinkConfig.format() != mDesiredFormat)) { - sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask, + sinkConfig = mAudioSink.buildConfig(mDesiredSamplingRate, mDesiredChannelMask, mDesiredFormat, null); shouldRecreateAudioPatch = true; } @@ -799,7 +818,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return false; } - int result = mHal.addStream(mInfo.getDeviceId(), surface, config); + int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config); return result == TvInputHal.SUCCESS; } } @@ -914,11 +933,18 @@ class TvInputHardwareManager implements TvInputHal.Callback { break; } case HDMI_DEVICE_UPDATED: { - SomeArgs args = (SomeArgs) msg.obj; - String inputId = (String) args.arg1; - HdmiDeviceInfo info = (HdmiDeviceInfo) args.arg2; - args.recycle(); - mListener.onHdmiDeviceUpdated(inputId, info); + HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; + String inputId = null; + synchronized (mLock) { + inputId = mHdmiInputIdMap.get(info.getId()); + } + if (inputId != null) { + mListener.onHdmiDeviceUpdated(inputId, info); + } else { + Slog.w(TAG, "Could not resolve input ID matching the device info; " + + "ignoring."); + } + break; } default: { Slog.w(TAG, "Unhandled message: " + msg); @@ -986,11 +1012,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { } mHdmiDeviceList.add(deviceInfo); messageType = ListenerHandler.HDMI_DEVICE_UPDATED; - String inputId = mHdmiInputIdMap.get(deviceInfo.getId()); - SomeArgs args = SomeArgs.obtain(); - args.arg1 = inputId; - args.arg2 = deviceInfo; - obj = args; + obj = deviceInfo; break; } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index fa1c0ff..f947b6a 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -346,8 +346,7 @@ final class AccessibilityController { case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: - case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: - case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: { + case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { Rect magnifiedRegionBounds = mTempRect2; mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( magnifiedRegionBounds); @@ -993,8 +992,7 @@ final class AccessibilityController { final int flags = windowState.mAttrs.flags; - // If the window is not touchable, do not report it but take into account - // the space it takes since the content behind it cannot be touched. + // If the window is not touchable - ignore. if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { continue; } @@ -1015,9 +1013,14 @@ final class AccessibilityController { } } - // Account for the space this window takes. - unaccountedSpace.op(boundsInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); + // Account for the space this window takes if the window + // is not an accessibility overlay which does not change + // the reported windows. + if (windowState.mAttrs.type == WindowManager.LayoutParams + .TYPE_ACCESSIBILITY_OVERLAY) { + unaccountedSpace.op(boundsInScreen, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + } // We figured out what is touchable for the entire screen - done. if (unaccountedSpace.isEmpty()) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 30589b1..b0feca8 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -376,10 +376,9 @@ class DisplayContent { stack.dump(prefix + " ", pw); } pw.println(); - pw.println(" Application tokens in bottom up Z order:"); + pw.println(" Application tokens in top down Z order:"); int ndx = 0; - final int numStacks = mStacks.size(); - for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { + for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) { ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { AppTokenList tokens = tasks.get(taskNdx).mAppTokens; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 69d3191..61ea1e8 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; +import static com.android.server.wm.WindowManagerService.DEBUG_KEYGUARD; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_UPDATE_ROTATION; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_MAY_CHANGE; import static com.android.server.wm.WindowManagerService.LayoutFields.SET_FORCE_HIDING_CHANGED; @@ -29,7 +30,6 @@ import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPA import android.content.Context; import android.os.Debug; import android.os.SystemClock; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; @@ -233,12 +233,17 @@ public class WindowAnimator { final WindowStateAnimator winAnimator = win.mWinAnimator; if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { if (!winAnimator.mAnimating) { + if (DEBUG_KEYGUARD) Slog.d(TAG, + "updateWindowsLocked: creating delay animation"); + // Create a new animation to delay until keyguard is gone on its own. winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f); winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS); winAnimator.mAnimationIsEntrance = false; } } else { + if (DEBUG_KEYGUARD) Slog.d(TAG, + "updateWindowsLocked: StatusBar is no longer keyguard"); mKeyguardGoingAway = false; winAnimator.clearAnimation(); } @@ -282,7 +287,7 @@ public class WindowAnimator { if (mPolicy.isForceHiding(win.mAttrs)) { if (!wasAnimating && nowAnimating) { - if (WindowManagerService.DEBUG_ANIM || + if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_ANIM || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Animation started that could impact force hide: " + win); mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED; @@ -310,7 +315,7 @@ public class WindowAnimator { mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN; } } - if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, + if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Force hide " + forceHidingToString() + " hasSurface=" + win.mHasSurface + " policyVis=" + win.mPolicyVisibility @@ -327,12 +332,12 @@ public class WindowAnimator { && (!winAnimator.isAnimating() || hideWhenLocked)) || ((mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked)) { changed = win.hideLw(false, false); - if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG, - "Now policy hidden: " + win); + if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) + && changed) Slog.v(TAG, "Now policy hidden: " + win); } else { changed = win.showLw(false, false); - if (WindowManagerService.DEBUG_VISIBILITY && changed) Slog.v(TAG, - "Now policy shown: " + win); + if ((DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) + && changed) Slog.v(TAG, "Now policy shown: " + win); if (changed) { if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0 && win.isVisibleNow() /*w.isReadyForDisplay()*/) { @@ -404,6 +409,11 @@ public class WindowAnimator { if (!mKeyguardGoingAwayDisableWindowAnimations) { a = mPolicy.createForceHideEnterAnimation(wallpaperInUnForceHiding, mKeyguardGoingAwayToNotificationShade); + if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: created anim=" + a + + " for win=" + unForceHiding.get(i)); + } else { + if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: skipping anim for win=" + + unForceHiding.get(i)); } if (a != null) { final WindowStateAnimator winAnimator = unForceHiding.get(i); @@ -422,6 +432,7 @@ public class WindowAnimator { // Wallpaper is going away in un-force-hide motion, animate it as well. if (!wallpaperInUnForceHiding && wallpaper != null && !mKeyguardGoingAwayDisableWindowAnimations) { + if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away"); Animation a = mPolicy.createForceHideWallpaperExitAnimation( mKeyguardGoingAwayToNotificationShade); if (a != null) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2354369..a48d39f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -166,6 +166,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_FOCUS = false; static final boolean DEBUG_FOCUS_LIGHT = DEBUG_FOCUS || false; static final boolean DEBUG_ANIM = false; + static final boolean DEBUG_KEYGUARD = false; static final boolean DEBUG_LAYOUT = false; static final boolean DEBUG_RESIZE = false; static final boolean DEBUG_LAYERS = false; @@ -2337,6 +2338,11 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + if (type == TYPE_ACCESSIBILITY_OVERLAY) { + Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { @@ -2380,6 +2386,12 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + } else if (type == TYPE_ACCESSIBILITY_OVERLAY) { + if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { + Slog.w(TAG, "Attempted to add Accessibility overlay window with bad token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } } else if (token.appWindowToken != null) { Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type); // It is not valid to use an app token with other system types; we will @@ -2476,7 +2488,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - win.mWinAnimator.mEnterAnimationPending = true; + final WindowStateAnimator winAnimator = win.mWinAnimator; + winAnimator.mEnterAnimationPending = true; + winAnimator.mEnteringAnimation = true; if (displayContent.isDefaultDisplay) { mPolicy.getContentInsetHintLw(attrs, outContentInsets); @@ -2515,9 +2529,8 @@ public class WindowManagerService extends IWindowManager.Stub } mInputMonitor.updateInputWindowsLw(false /*force*/); - if (localLOGV) Slog.v( - TAG, "New client " + client.asBinder() - + ": window=" + win); + if (true || localLOGV) Slog.v(TAG, "addWindow: New client " + client.asBinder() + + ": window=" + win + " Callers=" + Debug.getCallers(5)); if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; @@ -2681,7 +2694,8 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.removeWindowLw(win); win.removeLocked(); - if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win); + if (true || DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win + + " Callers=" + Debug.getCallers(5)); mWindowMap.remove(win.mClient.asBinder()); if (win.mAppOp != AppOpsManager.OP_NONE) { mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); @@ -2986,8 +3000,8 @@ public class WindowManagerService extends IWindowManager.Stub return 0; } WindowStateAnimator winAnimator = win.mWinAnimator; - if (win.mRequestedWidth != requestedWidth - || win.mRequestedHeight != requestedHeight) { + if (viewVisibility != View.GONE && (win.mRequestedWidth != requestedWidth + || win.mRequestedHeight != requestedHeight)) { win.mLayoutNeeded = true; win.mRequestedWidth = requestedWidth; win.mRequestedHeight = requestedHeight; @@ -3088,6 +3102,7 @@ public class WindowManagerService extends IWindowManager.Stub if (oldVisibility == View.GONE) { winAnimator.mEnterAnimationPending = true; } + winAnimator.mEnteringAnimation = true; if (toBeDisplayed) { if (win.isDrawnLw() && okToDisplay()) { winAnimator.applyEnterAnimationLocked(); @@ -3156,6 +3171,7 @@ public class WindowManagerService extends IWindowManager.Stub } } else { winAnimator.mEnterAnimationPending = false; + winAnimator.mEnteringAnimation = false; if (winAnimator.mSurfaceControl != null) { if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win + ": mExiting=" + win.mExiting); @@ -5348,6 +5364,8 @@ public class WindowManagerService extends IWindowManager.Stub != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires DISABLE_KEYGUARD permission"); } + if (DEBUG_KEYGUARD) Slog.d(TAG, "keyguardGoingAway: disableWinAnim=" + + disableWindowAnimations + " kgToNotifShade=" + keyguardGoingToNotificationShade); synchronized (mWindowMap) { mAnimator.mKeyguardGoingAway = true; mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade; @@ -5357,12 +5375,15 @@ public class WindowManagerService extends IWindowManager.Stub } public void keyguardWaitingForActivityDrawn() { + if (DEBUG_KEYGUARD) Slog.d(TAG, "keyguardWaitingForActivityDrawn"); synchronized (mWindowMap) { mKeyguardWaitingForActivityDrawn = true; } } public void notifyActivityDrawnForKeyguard() { + if (DEBUG_KEYGUARD) Slog.d(TAG, "notifyActivityDrawnForKeyguard: waiting=" + + mKeyguardWaitingForActivityDrawn); synchronized (mWindowMap) { if (mKeyguardWaitingForActivityDrawn) { mPolicy.notifyActivityDrawnForKeyguardLw(); @@ -6038,6 +6059,10 @@ public class WindowManagerService extends IWindowManager.Stub while (true) { if (retryCount++ > 0) { + // Reset max/min layers on retries so we don't accidentally take a screenshot of a + // layer based on the previous try. + maxLayer = 0; + minLayer = Integer.MAX_VALUE; try { Thread.sleep(100); } catch (InterruptedException e) { @@ -6060,7 +6085,17 @@ public class WindowManagerService extends IWindowManager.Stub continue; } } else if (ws.mIsWallpaper) { - // Fall through. + if (appWin == null) { + // We have not ran across the target window yet, so it is probably + // behind the wallpaper. This can happen when the keyguard is up and + // all windows are moved behind the wallpaper. We don't want to + // include the wallpaper layer in the screenshot as it will coverup + // the layer of the target window. + continue; + } + // Fall through. The target window is in front of the wallpaper. For this + // case we want to include the wallpaper layer in the screenshot because + // the target window might have some transparent areas. } else if (appToken != null) { if (ws.mAppToken == null || ws.mAppToken.token != appToken) { // This app window is of no interest if it is not associated with the @@ -9378,7 +9413,6 @@ public class WindowManagerService extends IWindowManager.Stub final int type = attrs.type; if (canBeSeen && (type == TYPE_SYSTEM_DIALOG - || type == TYPE_RECENTS_OVERLAY || type == TYPE_SYSTEM_ERROR || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0)) { mInnerFields.mSyswin = true; @@ -11628,5 +11662,23 @@ public class WindowManagerService extends IWindowManager.Stub checkDrawnWindowsLocked(); } } + + @Override + public void addWindowToken(IBinder token, int type) { + WindowManagerService.this.addWindowToken(token, type); + } + + @Override + public void removeWindowToken(IBinder token, boolean removeWindows) { + synchronized(mWindowMap) { + if (removeWindows) { + WindowToken wtoken = mTokenMap.remove(token); + if (wtoken != null) { + wtoken.removeAllWindows(); + } + } + WindowManagerService.this.removeWindowToken(token); + } + } } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b4a7f04..f9efc80 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import android.app.AppOpsManager; @@ -227,13 +228,33 @@ final class WindowState implements WindowManagerPolicy.WindowState { final Rect mCompatFrame = new Rect(); final Rect mContainingFrame = new Rect(); + + final Rect mParentFrame = new Rect(); + + // The entire screen area of the device. final Rect mDisplayFrame = new Rect(); + + // The region of the display frame that the display type supports displaying content on. This + // is mostly a special case for TV where some displays don’t have the entire display usable. + // {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag can be used to allow + // window display contents to extend into the overscan region. final Rect mOverscanFrame = new Rect(); + + // The display frame minus the stable insets. This value is always constant regardless of if + // the status bar or navigation bar is visible. + final Rect mStableFrame = new Rect(); + + // The area not occupied by the status and navigation bars. So, if both status and navigation + // bars are visible, the decor frame is equal to the stable frame. + final Rect mDecorFrame = new Rect(); + + // Equal to the decor frame if the IME (e.g. keyboard) is not present. Equal to the decor frame + // minus the area occupied by the IME if the IME is present. final Rect mContentFrame = new Rect(); - final Rect mParentFrame = new Rect(); + + // Legacy stuff. Generally equal to the content frame expect when the IME for older apps + // displays hint text. final Rect mVisibleFrame = new Rect(); - final Rect mDecorFrame = new Rect(); - final Rect mStableFrame = new Rect(); boolean mContentChanged; @@ -704,9 +725,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { WindowState ws = this; WindowList windows = getWindowList(); while (true) { - if ((ws.mAttrs.privateFlags - & WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY) != 0) { - return (ws.mAttrs.flags & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0; + if (ws.mAttrs.needsMenuKey != WindowManager.LayoutParams.NEEDS_MENU_UNSET) { + return ws.mAttrs.needsMenuKey == WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE; } // If we reached the bottom of the range of windows we are considering, // assume no menu is needed. @@ -883,7 +903,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { */ boolean isVisibleNow() { return mHasSurface && mPolicyVisibility && !mAttachedHidden - && !mRootToken.hidden && !mExiting && !mDestroying; + && (!mRootToken.hidden || mAttrs.type == TYPE_APPLICATION_STARTING) + && !mExiting && !mDestroying; } /** diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0c727f3..fc3f2c2 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -40,6 +40,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Debug; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.view.Display; @@ -141,6 +142,11 @@ class WindowStateAnimator { // an enter animation. boolean mEnterAnimationPending; + /** Used to indicate that this window is undergoing an enter animation. Used for system + * windows to make the callback to View.dispatchOnWindowShownCallback(). Set when the + * window is first added or shown, cleared when the callback has been made. */ + boolean mEnteringAnimation; + boolean keyguardGoingAwayAnimation; /** This is set when there is no Surface */ @@ -428,6 +434,14 @@ class WindowStateAnimator { mWin.mChildWindows.get(i).mWinAnimator.finishExit(); } + if (mEnteringAnimation && mWin.mAppToken == null) { + try { + mEnteringAnimation = false; + mWin.mClient.dispatchWindowShown(); + } catch (RemoteException e) { + } + } + if (!mWin.mExiting) { return; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 2267123..1a672e6 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.os.IBinder; +import android.util.Slog; import java.io.PrintWriter; @@ -29,7 +30,7 @@ import java.io.PrintWriter; class WindowToken { // The window manager! final WindowManagerService service; - + // The actual token. final IBinder token; @@ -77,6 +78,15 @@ class WindowToken { explicit = _explicit; } + void removeAllWindows() { + for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { + WindowState win = windows.get(winNdx); + if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG, + "removeAllWindows: removing win=" + win); + win.mService.removeWindowLocked(win.mSession, win); + } + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("windows="); pw.println(windows); pw.print(prefix); pw.print("windowType="); pw.print(windowType); diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index d5abe0c..5cb0543 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -235,7 +235,7 @@ public: static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); - int addStream(int deviceId, int streamId, const sp<Surface>& surface); + int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface); int removeStream(int deviceId, int streamId); const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); @@ -312,7 +312,7 @@ JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { return new JTvInputHal(env, thiz, device); } -int JTvInputHal::addStream(int deviceId, int streamId, const sp<Surface>& surface) { +int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); if (connections.indexOfKey(streamId) < 0) { connections.add(streamId, Connection()); @@ -555,14 +555,14 @@ static jlong nativeOpen(JNIEnv* env, jobject thiz) { return (jlong)JTvInputHal::createInstance(env, thiz); } -static int nativeAddStream(JNIEnv* env, jclass clazz, +static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId, jint streamId, jobject jsurface) { JTvInputHal* tvInputHal = (JTvInputHal*)ptr; if (!jsurface) { return BAD_VALUE; } sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); - return tvInputHal->addStream(deviceId, streamId, surface); + return tvInputHal->addOrUpdateStream(deviceId, streamId, surface); } static int nativeRemoveStream(JNIEnv* env, jclass clazz, @@ -612,8 +612,8 @@ static JNINativeMethod gTvInputHalMethods[] = { /* name, signature, funcPtr */ { "nativeOpen", "()J", (void*) nativeOpen }, - { "nativeAddStream", "(JIILandroid/view/Surface;)I", - (void*) nativeAddStream }, + { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I", + (void*) nativeAddOrUpdateStream }, { "nativeRemoveStream", "(JII)I", (void*) nativeRemoveStream }, { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;", diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9ee44b9..308fcd8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4788,6 +4788,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Slog.e(LOG_TAG, "Failed to talk to AudioService.", re); } } + sendChangedNotification(userHandle); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 6009ffd..d7f6130 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -374,6 +374,8 @@ public final class SystemServer { mSystemServiceManager.startService(UsageStatsService.class); mActivityManagerService.setUsageStatsManager( LocalServices.getService(UsageStatsManagerInternal.class)); + // Update after UsageStatsService is available, needed before performBootDexOpt. + mPackageManagerService.getUsageStatsIfNoPackageUsageInfo(); // Tracks whether the updatable WebView is in a ready state and watches for update installs. mSystemServiceManager.startService(WebViewUpdateService.class); diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java index 9496cae..7ab3840 100644 --- a/services/print/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -55,7 +56,8 @@ final class RemotePrintSpooler { private static final boolean DEBUG = false; - private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000; + private static final long BIND_SPOOLER_SERVICE_TIMEOUT = + ("eng".equals(Build.TYPE)) ? 120000 : 10000; private final Object mLock = new Object(); diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index c3d4ed9..c115339 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -932,7 +932,6 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes(); expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes(); - expect(mSettings.getReportXtOverDev()).andReturn(true).anyTimes(); final Config config = new Config(bucketDuration, deleteAge, deleteAge); expect(mSettings.getDevConfig()).andReturn(config).anyTimes(); diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index a70ebf4..b631331 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -21,22 +21,15 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import com.android.internal.content.PackageHelper; +import android.test.AndroidTestCase; +import android.util.ArraySet; +import android.util.Log; + import com.android.internal.os.AtomicFile; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.HashSet; - -import android.os.Debug; -import android.os.Environment; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.storage.IMountService; -import android.test.AndroidTestCase; -import android.util.Log; public class PackageManagerSettingsTests extends AndroidTestCase { @@ -182,11 +175,11 @@ public class PackageManagerSettingsTests extends AndroidTestCase { assertEquals(COMPONENT_ENABLED_STATE_ENABLED, ps.getEnabled(1)); // Enable/Disable a component - HashSet<String> components = new HashSet<String>(); + ArraySet<String> components = new ArraySet<String>(); String component1 = PACKAGE_NAME_1 + "/.Component1"; components.add(component1); ps.setDisabledComponents(components, 0); - HashSet<String> componentsDisabled = ps.getDisabledComponents(0); + ArraySet<String> componentsDisabled = ps.getDisabledComponents(0); assertEquals(1, componentsDisabled.size()); assertEquals(component1, componentsDisabled.toArray()[0]); boolean hasEnabled = diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index cfa4436..11da380 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -39,6 +39,7 @@ class UsageStatsDatabase { private static final String TAG = "UsageStatsDatabase"; private static final boolean DEBUG = UsageStatsService.DEBUG; + private static final String BAK_SUFFIX = ".bak"; private final Object mLock = new Object(); private final File[] mIntervalDirs; @@ -95,11 +96,71 @@ class UsageStatsDatabase { } } + public interface CheckinAction { + boolean checkin(IntervalStats stats); + } + + /** + * Calls {@link CheckinAction#checkin(IntervalStats)} on the given {@link CheckinAction} + * for all {@link IntervalStats} that haven't been checked-in. + * If any of the calls to {@link CheckinAction#checkin(IntervalStats)} returns false or throws + * an exception, the check-in will be aborted. + * + * @param checkinAction The callback to run when checking-in {@link IntervalStats}. + * @return true if the check-in succeeded. + */ + public boolean checkinDailyFiles(CheckinAction checkinAction) { + synchronized (mLock) { + final TimeSparseArray<AtomicFile> files = + mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY]; + final int fileCount = files.size(); + int start = 0; + while (start < fileCount - 1) { + if (!files.valueAt(start).getBaseFile().getName().endsWith("-c")) { + break; + } + } + + if (start == fileCount - 1) { + return true; + } + + try { + IntervalStats stats = new IntervalStats(); + for (int i = start; i < fileCount - 1; i++) { + UsageStatsXml.read(files.valueAt(i), stats); + if (!checkinAction.checkin(stats)) { + return false; + } + } + } catch (IOException e) { + Slog.e(TAG, "Failed to check-in", e); + return false; + } + + // We have successfully checked-in the stats, so rename the files so that they + // are marked as checked-in. + for (int i = start; i < fileCount - 1; i++) { + final AtomicFile file = files.valueAt(i); + final File checkedInFile = new File(file.getBaseFile().getParent(), + file.getBaseFile().getName() + "-c"); + if (!file.getBaseFile().renameTo(checkedInFile)) { + // We must return success, as we've already marked some files as checked-in. + // It's better to repeat ourselves than to lose data. + Slog.e(TAG, "Failed to mark file " + file.getBaseFile().getPath() + + " as checked-in"); + return true; + } + } + } + return true; + } + private void indexFilesLocked() { final FilenameFilter backupFileFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { - return !name.endsWith(".bak"); + return !name.endsWith(BAK_SUFFIX); } }; @@ -383,10 +444,10 @@ class UsageStatsDatabase { if (files != null) { for (File f : files) { String path = f.getPath(); - if (path.endsWith(".bak")) { - f = new File(path.substring(0, path.length() - 4)); + if (path.endsWith(BAK_SUFFIX)) { + f = new File(path.substring(0, path.length() - BAK_SUFFIX.length())); } - long beginTime = Long.parseLong(f.getName()); + long beginTime = UsageStatsXml.parseBeginTime(f); if (beginTime < expiryTime) { new AtomicFile(f).delete(); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 2ed9745..485b2a2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -33,11 +33,11 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Binder; -import android.os.Debug; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -47,9 +47,12 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemService; import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Arrays; import java.util.List; @@ -176,7 +179,7 @@ public class UsageStatsService extends SystemService implements long currentTimeMillis) { UserUsageStatsService service = mUserState.get(userId); if (service == null) { - service = new UserUsageStatsService(userId, + service = new UserUsageStatsService(getContext(), userId, new File(mUsageStatsDir, Integer.toString(userId)), this); service.init(currentTimeMillis); mUserState.put(userId, service); @@ -319,6 +322,30 @@ public class UsageStatsService extends SystemService implements mHandler.removeMessages(MSG_FLUSH_TO_DISK); } + /** + * Called by the Binder stub. + */ + void dump(String[] args, PrintWriter pw) { + synchronized (mLock) { + IndentingPrintWriter idpw = new IndentingPrintWriter(pw, " "); + ArraySet<String> argSet = new ArraySet<>(); + argSet.addAll(Arrays.asList(args)); + + final int userCount = mUserState.size(); + for (int i = 0; i < userCount; i++) { + idpw.printPair("user", mUserState.keyAt(i)); + idpw.println(); + idpw.increaseIndent(); + if (argSet.contains("--checkin")) { + mUserState.valueAt(i).checkin(idpw); + } else { + mUserState.valueAt(i).dump(idpw); + } + idpw.decreaseIndent(); + } + } + } + class H extends Handler { public H(Looper looper) { super(looper); @@ -349,8 +376,12 @@ public class UsageStatsService extends SystemService implements private class BinderService extends IUsageStatsManager.Stub { private boolean hasPermission(String callingPackage) { + final int callingUid = Binder.getCallingUid(); + if (callingUid == Process.SYSTEM_UID) { + return true; + } final int mode = mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, - Binder.getCallingUid(), callingPackage); + callingUid, callingPackage); if (mode == AppOpsManager.MODE_DEFAULT) { // The default behavior here is to check if PackageManager has given the app // permission. @@ -417,6 +448,18 @@ public class UsageStatsService extends SystemService implements Binder.restoreCallingIdentity(token); } } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println("Permission Denial: can't dump UsageStats from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + + " without permission " + android.Manifest.permission.DUMP); + return; + } + UsageStatsService.this.dump(args, pw); + } } /** diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java index 9ce6d63..26148ce 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXml.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java @@ -24,21 +24,26 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import java.io.*; public class UsageStatsXml { private static final String TAG = "UsageStatsXml"; private static final int CURRENT_VERSION = 1; private static final String USAGESTATS_TAG = "usagestats"; private static final String VERSION_ATTR = "version"; + private static final String CHECKED_IN_SUFFIX = "-c"; public static long parseBeginTime(AtomicFile file) { - return Long.parseLong(file.getBaseFile().getName()); + return parseBeginTime(file.getBaseFile()); + } + + public static long parseBeginTime(File file) { + final String name = file.getName(); + if (name.endsWith(CHECKED_IN_SUFFIX)) { + return Long.parseLong( + name.substring(0, name.length() - CHECKED_IN_SUFFIX.length())); + } + return Long.parseLong(name); } public static void read(AtomicFile file, IntervalStats statsOut) throws IOException { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 4916ec2..6596781 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -23,9 +23,13 @@ import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.res.Configuration; import android.os.SystemClock; +import android.content.Context; +import android.text.format.DateUtils; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.usage.UsageStatsDatabase.StatCombiner; import java.io.File; @@ -43,7 +47,13 @@ class UserUsageStatsService { private static final String TAG = "UsageStatsService"; private static final boolean DEBUG = UsageStatsService.DEBUG; private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final int sDateFormatFlags = + DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR + | DateUtils.FORMAT_NUMERIC_DATE; + private final Context mContext; private final UsageStatsDatabase mDatabase; private final IntervalStats[] mCurrentStats; private boolean mStatsChanged = false; @@ -55,7 +65,8 @@ class UserUsageStatsService { void onStatsUpdated(); } - UserUsageStatsService(int userId, File usageStatsDir, StatsUpdatedListener listener) { + UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) { + mContext = context; mDailyExpiryDate = new UnixCalendar(0); mDatabase = new UsageStatsDatabase(usageStatsDir); mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT]; @@ -433,6 +444,117 @@ class UserUsageStatsService { tempCal.getTimeInMillis() + ")"); } + // + // -- DUMP related methods -- + // + + void checkin(final IndentingPrintWriter pw) { + mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() { + @Override + public boolean checkin(IntervalStats stats) { + printIntervalStats(pw, stats, false); + return true; + } + }); + } + + void dump(IndentingPrintWriter pw) { + // This is not a check-in, only dump in-memory stats. + for (int interval = 0; interval < mCurrentStats.length; interval++) { + pw.print("In-memory "); + pw.print(intervalToString(interval)); + pw.println(" stats"); + printIntervalStats(pw, mCurrentStats[interval], true); + } + } + + private String formatDateTime(long dateTime, boolean pretty) { + if (pretty) { + return "\"" + DateUtils.formatDateTime(mContext, dateTime, sDateFormatFlags) + "\""; + } + return Long.toString(dateTime); + } + + private String formatElapsedTime(long elapsedTime, boolean pretty) { + if (pretty) { + return "\"" + DateUtils.formatElapsedTime(elapsedTime / 1000) + "\""; + } + return Long.toString(elapsedTime); + } + + void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) { + if (prettyDates) { + pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext, + stats.beginTime, stats.endTime, sDateFormatFlags) + "\""); + } else { + pw.printPair("beginTime", stats.beginTime); + pw.printPair("endTime", stats.endTime); + } + pw.println(); + pw.increaseIndent(); + pw.println("packages"); + pw.increaseIndent(); + final ArrayMap<String, UsageStats> pkgStats = stats.packageStats; + final int pkgCount = pkgStats.size(); + for (int i = 0; i < pkgCount; i++) { + final UsageStats usageStats = pkgStats.valueAt(i); + pw.printPair("package", usageStats.mPackageName); + pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates)); + pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates)); + pw.println(); + } + pw.decreaseIndent(); + + pw.println("configurations"); + pw.increaseIndent(); + final ArrayMap<Configuration, ConfigurationStats> configStats = + stats.configurations; + final int configCount = configStats.size(); + for (int i = 0; i < configCount; i++) { + final ConfigurationStats config = configStats.valueAt(i); + pw.printPair("config", Configuration.resourceQualifierString(config.mConfiguration)); + pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates)); + pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates)); + pw.printPair("count", config.mActivationCount); + pw.println(); + } + pw.decreaseIndent(); + + pw.println("events"); + pw.increaseIndent(); + final TimeSparseArray<UsageEvents.Event> events = stats.events; + final int eventCount = events != null ? events.size() : 0; + for (int i = 0; i < eventCount; i++) { + final UsageEvents.Event event = events.valueAt(i); + pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates)); + pw.printPair("type", eventToString(event.mEventType)); + pw.printPair("package", event.mPackage); + if (event.mClass != null) { + pw.printPair("class", event.mClass); + } + if (event.mConfiguration != null) { + pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration)); + } + pw.println(); + } + pw.decreaseIndent(); + pw.decreaseIndent(); + } + + private static String intervalToString(int interval) { + switch (interval) { + case UsageStatsManager.INTERVAL_DAILY: + return "daily"; + case UsageStatsManager.INTERVAL_WEEKLY: + return "weekly"; + case UsageStatsManager.INTERVAL_MONTHLY: + return "monthly"; + case UsageStatsManager.INTERVAL_YEARLY: + return "yearly"; + default: + return "?"; + } + } private static String eventToString(int eventType) { switch (eventType) { |