diff options
Diffstat (limited to 'services')
35 files changed, 1433 insertions, 790 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 8d1d124..1dc2d7e 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -190,7 +190,7 @@ public class ConnectivityService extends IConnectivityManager.Stub /** Set of ifaces that are costly. */ private HashSet<String> mMeteredIfaces = Sets.newHashSet(); - private Context mContext; + final private Context mContext; private int mNetworkPreference; // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; @@ -344,6 +344,11 @@ public class ConnectivityService extends IConnectivityManager.Stub */ private static final int EVENT_PROMPT_UNVALIDATED = 29; + /** + * used internally to (re)configure mobile data always-on settings. + */ + private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30; + /** Handler used for internal events. */ final private InternalHandler mHandler; /** Handler used for incoming {@link NetworkStateTracker} events. */ @@ -374,7 +379,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private PacManager mPacManager = null; - private SettingsObserver mSettingsObserver; + final private SettingsObserver mSettingsObserver; private UserManager mUserManager; @@ -555,13 +560,12 @@ public class ConnectivityService extends IConnectivityManager.Stub INetworkStatsService statsService, INetworkPolicyManager policyManager) { if (DBG) log("ConnectivityService starting up"); - NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); - mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); - NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), - NetworkRequestInfo.REQUEST); - mNetworkRequests.put(mDefaultRequest, nri); + mDefaultRequest = createInternetRequestForTransport(-1); + mNetworkRequests.put(mDefaultRequest, new NetworkRequestInfo( + null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST)); + + mDefaultMobileDataRequest = createInternetRequestForTransport( + NetworkCapabilities.TRANSPORT_CELLULAR); HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); handlerThread.start(); @@ -696,8 +700,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mInetLog = new ArrayList(); } - mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY); - mSettingsObserver.observe(mContext); + mSettingsObserver = new SettingsObserver(mContext, mHandler); + registerSettingsCallbacks(); mDataConnectionStats = new DataConnectionStats(mContext); mDataConnectionStats.startMonitoring(); @@ -707,6 +711,44 @@ public class ConnectivityService extends IConnectivityManager.Stub mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); } + private NetworkRequest createInternetRequestForTransport(int transportType) { + NetworkCapabilities netCap = new NetworkCapabilities(); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + if (transportType > -1) { + netCap.addTransportType(transportType); + } + return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); + } + + private void handleMobileDataAlwaysOn() { + final boolean enable = (Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1); + final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null); + if (enable == isEnabled) { + return; // Nothing to do. + } + + if (enable) { + handleRegisterNetworkRequest(new NetworkRequestInfo( + null, mDefaultMobileDataRequest, new Binder(), NetworkRequestInfo.REQUEST)); + } else { + handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID); + } + } + + private void registerSettingsCallbacks() { + // Watch for global HTTP proxy changes. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.HTTP_PROXY), + EVENT_APPLY_GLOBAL_HTTP_PROXY); + + // Watch for whether or not to keep mobile data always on. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), + EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + } + private synchronized int nextNetworkRequestId() { return mNextNetworkRequestId++; } @@ -1491,6 +1533,9 @@ public class ConnectivityService extends IConnectivityManager.Stub mContext.registerReceiver(mUserPresentReceiver, filter); } + // Configure whether mobile data is always on. + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON)); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY)); mPermissionMonitor.startMonitoring(); @@ -2107,12 +2152,10 @@ public class ConnectivityService extends IConnectivityManager.Stub + nri.request + " because their intents matched."); handleReleaseNetworkRequest(existingRequest.request, getCallingUid()); } - handleRegisterNetworkRequest(msg); + handleRegisterNetworkRequest(nri); } - private void handleRegisterNetworkRequest(Message msg) { - final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj); - + private void handleRegisterNetworkRequest(NetworkRequestInfo nri) { mNetworkRequests.put(nri.request, nri); // TODO: This logic may be better replaced with a call to rematchNetworkAndRequests @@ -2423,7 +2466,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } case EVENT_REGISTER_NETWORK_REQUEST: case EVENT_REGISTER_NETWORK_LISTENER: { - handleRegisterNetworkRequest(msg); + handleRegisterNetworkRequest((NetworkRequestInfo) msg.obj); break; } case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: { @@ -2446,6 +2489,10 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePromptUnvalidated((Network) msg.obj); break; } + case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: { + handleMobileDataAlwaysOn(); + break; + } case EVENT_SYSTEM_READY: { for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) { nai.networkMonitor.systemReady = true; @@ -2837,23 +2884,36 @@ public class ConnectivityService extends IConnectivityManager.Stub } private static class SettingsObserver extends ContentObserver { - private int mWhat; - private Handler mHandler; - SettingsObserver(Handler handler, int what) { - super(handler); + final private HashMap<Uri, Integer> mUriEventMap; + final private Context mContext; + final private Handler mHandler; + + SettingsObserver(Context context, Handler handler) { + super(null); + mUriEventMap = new HashMap<Uri, Integer>(); + mContext = context; mHandler = handler; - mWhat = what; } - void observe(Context context) { - ContentResolver resolver = context.getContentResolver(); - resolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.HTTP_PROXY), false, this); + void observe(Uri uri, int what) { + mUriEventMap.put(uri, what); + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(uri, false, this); } @Override public void onChange(boolean selfChange) { - mHandler.obtainMessage(mWhat).sendToTarget(); + Slog.wtf(TAG, "Should never be reached."); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + final Integer what = mUriEventMap.get(uri); + if (what != null) { + mHandler.obtainMessage(what.intValue()).sendToTarget(); + } else { + loge("No matching event to send for URI=" + uri); + } } } @@ -3643,6 +3703,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated. private final NetworkRequest mDefaultRequest; + // Request used to optionally keep mobile data active even when higher + // priority networks like Wi-Fi are active. + private final NetworkRequest mDefaultMobileDataRequest; + private NetworkAgentInfo getDefaultNetwork() { return mNetworkForRequestId.get(mDefaultRequest.requestId); } diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index b7bc0f0..76465d4 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -142,6 +142,7 @@ public class DeviceIdleController extends SystemService { private PendingIntent mAlarmIntent; private Intent mIdleIntent; private Display mCurDisplay; + private boolean mIdleDisabled; private boolean mScreenOn; private boolean mCharging; private boolean mSigMotionActive; @@ -187,10 +188,16 @@ public class DeviceIdleController extends SystemService { private final ArrayMap<String, Integer> mPowerSaveWhitelistUserApps = new ArrayMap<>(); /** - * UIDs that have been white-listed to opt out of power save restrictions. + * App IDs that have been white-listed to opt out of power save restrictions. */ private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray(); + /** + * Current app IDs that are in the complete power save white list. This array can + * be shared with others because it will not be modified once set. + */ + private int[] mPowerSaveWhitelistAppIdArray = new int[0]; + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { @@ -381,6 +388,8 @@ public class DeviceIdleController extends SystemService { filter.addAction(ACTION_STEP_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); + mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray); + mDisplayManager.registerDisplayListener(mDisplayListener, null); updateDisplayLocked(); } @@ -445,12 +454,7 @@ public class DeviceIdleController extends SystemService { public int[] getAppIdWhitelistInternal() { synchronized (this) { - int size = mPowerSaveWhitelistAppIds.size(); - int[] appids = new int[size]; - for (int i = 0; i < size; i++) { - appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); - } - return appids; + return mPowerSaveWhitelistAppIdArray; } } @@ -499,7 +503,7 @@ public class DeviceIdleController extends SystemService { } void becomeInactiveIfAppropriateLocked() { - if (!mScreenOn && !mCharging && mState == STATE_ACTIVE) { + if (!mScreenOn && !mCharging && !mIdleDisabled && mState == STATE_ACTIVE) { // Screen has turned off; we are now going to become inactive and start // waiting to see if we will ultimately go idle. mState = STATE_INACTIVE; @@ -625,6 +629,15 @@ public class DeviceIdleController extends SystemService { for (int i=0; i<mPowerSaveWhitelistUserApps.size(); i++) { mPowerSaveWhitelistAppIds.put(mPowerSaveWhitelistUserApps.valueAt(i), true); } + int size = mPowerSaveWhitelistAppIds.size(); + int[] appids = new int[size]; + for (int i = 0; i < size; i++) { + appids[i] = mPowerSaveWhitelistAppIds.keyAt(i); + } + mPowerSaveWhitelistAppIdArray = appids; + if (mLocalPowerManager != null) { + mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray); + } } private void reportPowerSaveWhitelistChangedLocked() { @@ -763,6 +776,10 @@ public class DeviceIdleController extends SystemService { pw.println("Commands:"); pw.println(" step"); pw.println(" Immediately step to next state, without waiting for alarm."); + pw.println(" disable"); + pw.println(" Completely disable device idle mode."); + pw.println(" enable"); + pw.println(" Re-enable device idle mode after it had previously been disabled."); pw.println(" whitelist"); pw.println(" Add (prefix with +) or remove (prefix with -) packages."); } @@ -782,12 +799,32 @@ public class DeviceIdleController extends SystemService { if ("-h".equals(arg)) { dumpHelp(pw); return; + } else if ("-a".equals(arg)) { + // Ignore, we always dump all. } else if ("step".equals(arg)) { synchronized (this) { stepIdleStateLocked(); pw.print("Stepped to: "); pw.println(stateToString(mState)); } return; + } else if ("disable".equals(arg)) { + synchronized (this) { + if (!mIdleDisabled) { + mIdleDisabled = true; + becomeActiveLocked("disabled"); + pw.println("Idle mode disabled"); + } + } + return; + } else if ("enable".equals(arg)) { + synchronized (this) { + if (mIdleDisabled) { + mIdleDisabled = false; + becomeInactiveIfAppropriateLocked(); + pw.println("Idle mode enabled"); + } + } + return; } else if ("whitelist".equals(arg)) { i++; while (i < args.length) { @@ -853,6 +890,7 @@ public class DeviceIdleController extends SystemService { } pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor); pw.print(" mCurDisplay="); pw.println(mCurDisplay); + pw.print(" mIdleDisabled="); pw.println(mIdleDisabled); pw.print(" mScreenOn="); pw.println(mScreenOn); pw.print(" mCharging="); pw.println(mCharging); pw.print(" mSigMotionActive="); pw.println(mSigMotionActive); diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 9511f54..e856a93 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -1695,6 +1695,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub vis = 0; } mImeWindowVis = vis; + mInputShown = ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0); mBackDisposition = backDisposition; final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0) && (mWindowManagerService.isHardKeyboardAvailable() diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 5df74c5..ed2de4a 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -356,28 +356,23 @@ public class LockSettingsService extends ILockSettings.Stub { return mStorage.hasPattern(userId); } - private void maybeUpdateKeystore(String password, int userHandle) { + private void setKeystorePassword(String password, int userHandle) { final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); final KeyStore ks = KeyStore.getInstance(); final List<UserInfo> profiles = um.getProfiles(userHandle); - boolean shouldReset = TextUtils.isEmpty(password); - - // For historical reasons, don't wipe a non-empty keystore if we have a single user with a - // single profile. - if (userHandle == UserHandle.USER_OWNER && profiles.size() == 1) { - if (!ks.isEmpty()) { - shouldReset = false; - } + for (UserInfo pi : profiles) { + ks.onUserPasswordChanged(pi.id, password); } + } + + private void unlockKeystore(String password, int userHandle) { + final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE); + final KeyStore ks = KeyStore.getInstance(); + final List<UserInfo> profiles = um.getProfiles(userHandle); for (UserInfo pi : profiles) { - final int profileUid = UserHandle.getUid(pi.id, Process.SYSTEM_UID); - if (shouldReset) { - ks.resetUid(profileUid); - } else { - ks.passwordUid(password, profileUid); - } + ks.unlock(pi.id, password); } } @@ -423,7 +418,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (pattern == null) { getGateKeeperService().clearSecureUserId(userId); mStorage.writePatternHash(null, userId); - maybeUpdateKeystore(null, userId); + setKeystorePassword(null, userId); return; } @@ -451,7 +446,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (password == null) { getGateKeeperService().clearSecureUserId(userId); mStorage.writePasswordHash(null, userId); - maybeUpdateKeystore(null, userId); + setKeystorePassword(null, userId); return; } @@ -484,7 +479,7 @@ public class LockSettingsService extends ILockSettings.Stub { toEnrollBytes); if (hash != null) { - maybeUpdateKeystore(toEnroll, userId); + setKeystorePassword(toEnroll, userId); } return hash; @@ -530,7 +525,7 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] hash = mLockPatternUtils.patternToHash( mLockPatternUtils.stringToPattern(pattern)); if (Arrays.equals(hash, storedHash.hash)) { - maybeUpdateKeystore(pattern, userId); + unlockKeystore(pattern, userId); // migrate password to GateKeeper setLockPattern(pattern, null, userId); if (!hasChallenge) { @@ -556,7 +551,7 @@ public class LockSettingsService extends ILockSettings.Stub { } // pattern has matched - maybeUpdateKeystore(pattern, userId); + unlockKeystore(pattern, userId); return token; } @@ -599,7 +594,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (storedHash.version == CredentialHash.VERSION_LEGACY) { byte[] hash = mLockPatternUtils.passwordToHash(password, userId); if (Arrays.equals(hash, storedHash.hash)) { - maybeUpdateKeystore(password, userId); + unlockKeystore(password, userId); // migrate password to GateKeeper setLockPassword(password, null, userId); if (!hasChallenge) { @@ -625,7 +620,7 @@ public class LockSettingsService extends ILockSettings.Stub { } // password has matched - maybeUpdateKeystore(password, userId); + unlockKeystore(password, userId); return token; } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 0925fa5..93ac51a 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -242,6 +242,7 @@ class MountService extends IMountService.Stub private static final int VERSION_INIT = 1; private static final int VERSION_ADD_PRIMARY = 2; + private static final int VERSION_FIX_PRIMARY = 3; private static final String TAG_VOLUMES = "volumes"; private static final String ATTR_VERSION = "version"; @@ -1187,8 +1188,17 @@ class MountService extends IMountService.Stub mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); } + private String getDefaultPrimaryStorageUuid() { + if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) { + return StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + return StorageManager.UUID_PRIVATE_INTERNAL; + } + } + private void readSettingsLocked() { mRecords.clear(); + mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); FileInputStream fis = null; try { @@ -1202,16 +1212,13 @@ class MountService extends IMountService.Stub final String tag = in.getName(); if (TAG_VOLUMES.equals(tag)) { final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); - if (version >= VERSION_ADD_PRIMARY) { + final boolean primaryPhysical = SystemProperties.getBoolean( + StorageManager.PROP_PRIMARY_PHYSICAL, false); + final boolean validAttr = (version >= VERSION_FIX_PRIMARY) + || (version >= VERSION_ADD_PRIMARY && !primaryPhysical); + if (validAttr) { mPrimaryStorageUuid = readStringAttribute(in, ATTR_PRIMARY_STORAGE_UUID); - } else { - if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, - false)) { - mPrimaryStorageUuid = StorageManager.UUID_PRIMARY_PHYSICAL; - } else { - mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; - } } } else if (TAG_VOLUME.equals(tag)) { @@ -1240,7 +1247,7 @@ class MountService extends IMountService.Stub out.setOutput(fos, "utf-8"); out.startDocument(null, true); out.startTag(null, TAG_VOLUMES); - writeIntAttribute(out, ATTR_VERSION, VERSION_ADD_PRIMARY); + writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); final int size = mRecords.size(); for (int i = 0; i < size; i++) { @@ -1482,7 +1489,7 @@ class MountService extends IMountService.Stub // If this had been primary storage, revert back to internal and // reset vold so we bind into new volume into place. if (Objects.equals(mPrimaryStorageUuid, fsUuid)) { - mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; + mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); resetIfReadyAndConnected(); } @@ -1497,11 +1504,13 @@ class MountService extends IMountService.Stub final String fsUuid = mRecords.keyAt(i); mCallbacks.notifyVolumeForgotten(fsUuid); } - mRecords.clear(); - writeSettingsLocked(); - mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; + if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) { + mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); + } + + writeSettingsLocked(); resetIfReadyAndConnected(); } } @@ -1522,13 +1531,8 @@ class MountService extends IMountService.Stub waitForReady(); synchronized (mLock) { - final VolumeInfo from = Preconditions.checkNotNull( - findStorageForUuid(mPrimaryStorageUuid)); - final VolumeInfo to = Preconditions.checkNotNull( - findStorageForUuid(volumeUuid)); - - if (Objects.equals(from, to)) { - throw new IllegalArgumentException("Primary storage already at " + from); + if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) { + throw new IllegalArgumentException("Primary storage already at " + volumeUuid); } if (mMoveCallback != null) { @@ -1537,10 +1541,26 @@ class MountService extends IMountService.Stub mMoveCallback = callback; mMoveTargetUuid = volumeUuid; - try { - mConnector.execute("volume", "move_storage", from.id, to.id); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + // When moving to/from primary physical volume, we probably just nuked + // the current storage location, so we have nothing to move. + if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) + || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { + Slog.d(TAG, "Skipping move to/from primary physical"); + onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED); + onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED); + resetIfReadyAndConnected(); + + } else { + final VolumeInfo from = Preconditions.checkNotNull( + findStorageForUuid(mPrimaryStorageUuid)); + final VolumeInfo to = Preconditions.checkNotNull( + findStorageForUuid(volumeUuid)); + + try { + mConnector.execute("volume", "move_storage", from.id, to.id); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } } } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3dece49..fa4d204 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import android.app.ActivityThread; import android.os.Build; @@ -119,14 +120,13 @@ public final class ActiveServices { // at the same time. final int mMaxStartingBackground; - final SparseArray<ServiceMap> mServiceMap = new SparseArray<ServiceMap>(); + final SparseArray<ServiceMap> mServiceMap = new SparseArray<>(); /** * All currently bound service connections. Keys are the IBinder of * the client's IServiceConnection. */ - final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections - = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>(); + final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>(); /** * List of services that we have been asked to start, @@ -134,20 +134,20 @@ public final class ActiveServices { * while waiting for their corresponding application thread to get * going. */ - final ArrayList<ServiceRecord> mPendingServices - = new ArrayList<ServiceRecord>(); + final ArrayList<ServiceRecord> mPendingServices = new ArrayList<>(); /** * List of services that are scheduled to restart following a crash. */ - final ArrayList<ServiceRecord> mRestartingServices - = new ArrayList<ServiceRecord>(); + final ArrayList<ServiceRecord> mRestartingServices = new ArrayList<>(); /** * List of services that are in the process of being destroyed. */ - final ArrayList<ServiceRecord> mDestroyingServices - = new ArrayList<ServiceRecord>(); + final ArrayList<ServiceRecord> mDestroyingServices = new ArrayList<>(); + + /** Temporary list for holding the results of calls to {@link #collectPackageServicesLocked} */ + private ArrayList<ServiceRecord> mTmpCollectionResults = null; /** Amount of time to allow a last ANR message to exist before freeing the memory. */ static final int LAST_ANR_LIFETIME_DURATION_MSECS = 2 * 60 * 60 * 1000; // Two hours @@ -162,10 +162,6 @@ public final class ActiveServices { } }; - static final class DelayingProcess extends ArrayList<ServiceRecord> { - long timeoout; - } - /** * Information about services for a single user. */ @@ -2076,14 +2072,16 @@ public final class ActiveServices { } } - private boolean collectForceStopServicesLocked(String name, int userId, - boolean evenPersistent, boolean doit, - ArrayMap<ComponentName, ServiceRecord> services, - ArrayList<ServiceRecord> result) { + private boolean collectPackageServicesLocked(String packageName, Set<String> filterByClasses, + boolean evenPersistent, boolean doit, ArrayMap<ComponentName, ServiceRecord> services) { boolean didSomething = false; - for (int i=0; i<services.size(); i++) { + for (int i = services.size() - 1; i >= 0; i--) { ServiceRecord service = services.valueAt(i); - if ((name == null || service.packageName.equals(name)) + final boolean sameComponent = packageName == null + || (service.packageName.equals(packageName) + && (filterByClasses == null + || filterByClasses.contains(service.name.getClassName()))); + if (sameComponent && (service.app == null || evenPersistent || !service.app.persistent)) { if (!doit) { return true; @@ -2098,19 +2096,27 @@ public final class ActiveServices { } service.app = null; service.isolatedProc = null; - result.add(service); + if (mTmpCollectionResults == null) { + mTmpCollectionResults = new ArrayList<>(); + } + mTmpCollectionResults.add(service); } } return didSomething; } - boolean forceStopLocked(String name, int userId, boolean evenPersistent, boolean doit) { + boolean bringDownDisabledPackageServicesLocked(String packageName, Set<String> filterByClasses, + int userId, boolean evenPersistent, boolean doit) { boolean didSomething = false; - ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + + if (mTmpCollectionResults != null) { + mTmpCollectionResults.clear(); + } + if (userId == UserHandle.USER_ALL) { - for (int i=0; i<mServiceMap.size(); i++) { - didSomething |= collectForceStopServicesLocked(name, userId, evenPersistent, - doit, mServiceMap.valueAt(i).mServicesByName, services); + for (int i = mServiceMap.size() - 1; i >= 0; i--) { + didSomething |= collectPackageServicesLocked(packageName, filterByClasses, + evenPersistent, doit, mServiceMap.valueAt(i).mServicesByName); if (!doit && didSomething) { return true; } @@ -2119,22 +2125,24 @@ public final class ActiveServices { ServiceMap smap = mServiceMap.get(userId); if (smap != null) { ArrayMap<ComponentName, ServiceRecord> items = smap.mServicesByName; - didSomething = collectForceStopServicesLocked(name, userId, evenPersistent, - doit, items, services); + didSomething = collectPackageServicesLocked(packageName, filterByClasses, + evenPersistent, doit, items); } } - int N = services.size(); - for (int i=0; i<N; i++) { - bringDownServiceLocked(services.get(i)); + if (mTmpCollectionResults != null) { + for (int i = mTmpCollectionResults.size() - 1; i >= 0; i--) { + bringDownServiceLocked(mTmpCollectionResults.get(i)); + } + mTmpCollectionResults.clear(); } return didSomething; } void cleanUpRemovedTaskLocked(TaskRecord tr, ComponentName component, Intent baseIntent) { - ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + ArrayList<ServiceRecord> services = new ArrayList<>(); ArrayMap<ComponentName, ServiceRecord> alls = getServices(tr.userId); - for (int i=0; i<alls.size(); i++) { + for (int i = alls.size() - 1; i >= 0; i--) { ServiceRecord sr = alls.valueAt(i); if (sr.packageName.equals(component.getPackageName())) { services.add(sr); @@ -2142,7 +2150,7 @@ public final class ActiveServices { } // Take care of any running services associated with the app. - for (int i=0; i<services.size(); i++) { + for (int i = services.size() - 1; i >= 0; i--) { ServiceRecord sr = services.get(i); if (sr.startRequested) { if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 82dbfee..76ee3bc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -375,6 +375,10 @@ public final class ActivityManagerService extends ActivityManagerNative // Delay in notifying task stack change listeners (in millis) static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 1000; + // Necessary ApplicationInfo flags to mark an app as persistent + private static final int PERSISTENT_MASK = + ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT; + /** All system services */ SystemServiceManager mSystemServiceManager; @@ -5363,39 +5367,117 @@ public final class ActivityManagerService extends ActivityManagerNative return N > 0; } - private final boolean forceStopPackageLocked(String name, int appId, + private void cleanupDisabledPackageComponentsLocked( + String packageName, int userId, String[] changedClasses) { + + Set<String> disabledClasses = null; + boolean packageDisabled = false; + IPackageManager pm = AppGlobals.getPackageManager(); + + if (changedClasses == null) { + // Nothing changed... + return; + } + + // Determine enable/disable state of the package and its components. + int enabled = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + for (int i = changedClasses.length - 1; i >= 0; i--) { + final String changedClass = changedClasses[i]; + + if (changedClass.equals(packageName)) { + try { + // Entire package setting changed + enabled = pm.getApplicationEnabledSetting(packageName, + (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER); + } catch (RemoteException e) { + // Can't happen... + } + packageDisabled = enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED + && enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + if (packageDisabled) { + // Entire package is disabled. + // No need to continue to check component states. + disabledClasses = null; + break; + } + } else { + try { + enabled = pm.getComponentEnabledSetting( + new ComponentName(packageName, changedClass), + (userId != UserHandle.USER_ALL) ? userId : UserHandle.USER_OWNER); + } catch (RemoteException e) { + // Can't happen... + } + if (enabled != PackageManager.COMPONENT_ENABLED_STATE_ENABLED + && enabled != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { + if (disabledClasses == null) { + disabledClasses = new ArraySet<>(changedClasses.length); + } + disabledClasses.add(changedClass); + } + } + } + + if (!packageDisabled && disabledClasses == null) { + // Nothing to do here... + return; + } + + // Clean-up disabled activities. + if (mStackSupervisor.finishDisabledPackageActivitiesLocked( + packageName, disabledClasses, true, false, userId) && mBooted) { + mStackSupervisor.resumeTopActivitiesLocked(); + mStackSupervisor.scheduleIdleLocked(); + } + + // Clean-up disabled tasks + cleanupDisabledPackageTasksLocked(packageName, disabledClasses, userId); + + // Clean-up disabled services. + mServices.bringDownDisabledPackageServicesLocked( + packageName, disabledClasses, userId, false, true); + + // Clean-up disabled providers. + ArrayList<ContentProviderRecord> providers = new ArrayList<>(); + mProviderMap.collectPackageProvidersLocked( + packageName, disabledClasses, true, false, userId, providers); + for (int i = providers.size() - 1; i >= 0; i--) { + removeDyingProviderLocked(null, providers.get(i), true); + } + } + + private final boolean forceStopPackageLocked(String packageName, int appId, boolean callerWillRestart, boolean purgeCache, boolean doit, boolean evenPersistent, boolean uninstalling, int userId, String reason) { int i; - int N; - if (userId == UserHandle.USER_ALL && name == null) { + if (userId == UserHandle.USER_ALL && packageName == null) { Slog.w(TAG, "Can't force stop all processes of all users, that is insane!"); } - if (appId < 0 && name != null) { + if (appId < 0 && packageName != null) { try { appId = UserHandle.getAppId( - AppGlobals.getPackageManager().getPackageUid(name, 0)); + AppGlobals.getPackageManager().getPackageUid(packageName, 0)); } catch (RemoteException e) { } } if (doit) { - if (name != null) { - Slog.i(TAG, "Force stopping " + name + " appid=" + appId + if (packageName != null) { + Slog.i(TAG, "Force stopping " + packageName + " appid=" + appId + " user=" + userId + ": " + reason); } else { Slog.i(TAG, "Force stopping u" + userId + ": " + reason); } final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - for (int ip=pmap.size()-1; ip>=0; ip--) { + for (int ip = pmap.size() - 1; ip >= 0; ip--) { SparseArray<Long> ba = pmap.valueAt(ip); - for (i=ba.size()-1; i>=0; i--) { + for (i = ba.size() - 1; i >= 0; i--) { boolean remove = false; final int entUid = ba.keyAt(i); - if (name != null) { + if (packageName != null) { if (userId == UserHandle.USER_ALL) { if (UserHandle.getAppId(entUid) == appId) { remove = true; @@ -5418,46 +5500,47 @@ public final class ActivityManagerService extends ActivityManagerNative } } - boolean didSomething = killPackageProcessesLocked(name, appId, userId, + boolean didSomething = killPackageProcessesLocked(packageName, appId, userId, -100, callerWillRestart, true, doit, evenPersistent, - name == null ? ("stop user " + userId) : ("stop " + name)); + packageName == null ? ("stop user " + userId) : ("stop " + packageName)); - if (mStackSupervisor.forceStopPackageLocked(name, doit, evenPersistent, userId)) { + if (mStackSupervisor.finishDisabledPackageActivitiesLocked( + packageName, null, doit, evenPersistent, userId)) { if (!doit) { return true; } didSomething = true; } - if (mServices.forceStopLocked(name, userId, evenPersistent, doit)) { + if (mServices.bringDownDisabledPackageServicesLocked( + packageName, null, userId, evenPersistent, doit)) { if (!doit) { return true; } didSomething = true; } - if (name == null) { + if (packageName == null) { // Remove all sticky broadcasts from this user. mStickyBroadcasts.remove(userId); } - ArrayList<ContentProviderRecord> providers = new ArrayList<ContentProviderRecord>(); - if (mProviderMap.collectForceStopProviders(name, appId, doit, evenPersistent, + ArrayList<ContentProviderRecord> providers = new ArrayList<>(); + if (mProviderMap.collectPackageProvidersLocked(packageName, null, doit, evenPersistent, userId, providers)) { if (!doit) { return true; } didSomething = true; } - N = providers.size(); - for (i=0; i<N; i++) { + for (i = providers.size() - 1; i >= 0; i--) { removeDyingProviderLocked(null, providers.get(i), true); } // Remove transient permissions granted from/to this package/user - removeUriPermissionsForPackageLocked(name, userId, false); + removeUriPermissionsForPackageLocked(packageName, userId, false); - if (name == null || uninstalling) { + if (packageName == null || uninstalling) { // Remove pending intents. For now we only do this when force // stopping users, because we have some problems when doing this // for packages -- app widgets are not currently cleaned up for @@ -5476,7 +5559,7 @@ public final class ActivityManagerService extends ActivityManagerNative it.remove(); continue; } - if (name == null) { + if (packageName == null) { // Stopping user, remove all objects for the user. if (pir.key.userId != userId) { // Not the same user, skip it. @@ -5491,7 +5574,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Different user, skip it. continue; } - if (!pir.key.packageName.equals(name)) { + if (!pir.key.packageName.equals(packageName)) { // Different package, skip it. continue; } @@ -5510,10 +5593,10 @@ public final class ActivityManagerService extends ActivityManagerNative } if (doit) { - if (purgeCache && name != null) { + if (purgeCache && packageName != null) { AttributeCache ac = AttributeCache.instance(); if (ac != null) { - ac.removePackage(name); + ac.removePackage(packageName); } } if (mBooted) { @@ -8331,29 +8414,20 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private void removeTasksByRemovedPackageComponentsLocked(String packageName, int userId) { - final IPackageManager pm = AppGlobals.getPackageManager(); - final HashSet<ComponentName> componentsKnownToExist = new HashSet<ComponentName>(); + private void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses, + int userId) { for (int i = mRecentTasks.size() - 1; i >= 0; i--) { TaskRecord tr = mRecentTasks.get(i); - if (tr.userId != userId) continue; + if (userId != UserHandle.USER_ALL && tr.userId != userId) { + continue; + } ComponentName cn = tr.intent.getComponent(); - if (cn != null && cn.getPackageName().equals(packageName)) { - // Skip if component still exists in the package. - if (componentsKnownToExist.contains(cn)) continue; - - try { - ActivityInfo info = pm.getActivityInfo(cn, 0, userId); - if (info != null) { - componentsKnownToExist.add(cn); - } else { - removeTaskByIdLocked(tr.taskId, false); - } - } catch (RemoteException e) { - Log.e(TAG, "Activity info query failed. component=" + cn, e); - } + final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName) + && (filterByClasses == null || filterByClasses.contains(cn.getClassName())); + if (sameComponent) { + removeTaskByIdLocked(tr.taskId, false); } } } @@ -9773,10 +9847,10 @@ public final class ActivityManagerService extends ActivityManagerNative String proc = customProcess != null ? customProcess : info.processName; BatteryStatsImpl.Uid.Proc ps = null; BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); + final int userId = UserHandle.getUserId(info.uid); int uid = info.uid; if (isolated) { if (isolatedUid == 0) { - int userId = UserHandle.getUserId(uid); int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1; while (true) { if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID @@ -9800,7 +9874,13 @@ public final class ActivityManagerService extends ActivityManagerNative uid = isolatedUid; } } - return new ProcessRecord(stats, info, proc, uid); + final ProcessRecord r = new ProcessRecord(stats, info, proc, uid); + if (!mBooted && !mBooting + && userId == UserHandle.USER_OWNER + && (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) { + r.persistent = true; + } + return r; } final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated, @@ -9832,8 +9912,7 @@ public final class ActivityManagerService extends ActivityManagerNative + info.packageName + ": " + e); } - if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) - == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) { + if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) { app.persistent = true; app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ; } @@ -14121,6 +14200,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpDetails = false; boolean dumpFullDetails = false; boolean dumpDalvik = false; + boolean dumpSummaryOnly = false; boolean oomOnly = false; boolean isCompact = false; boolean localOnly = false; @@ -14141,6 +14221,9 @@ public final class ActivityManagerService extends ActivityManagerNative dumpDalvik = true; } else if ("-c".equals(opt)) { isCompact = true; + } else if ("-s".equals(opt)) { + dumpDetails = true; + dumpSummaryOnly = true; } else if ("--oom".equals(opt)) { oomOnly = true; } else if ("--local".equals(opt)) { @@ -14148,10 +14231,11 @@ public final class ActivityManagerService extends ActivityManagerNative } else if ("--package".equals(opt)) { packages = true; } else if ("-h".equals(opt)) { - pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]"); + pw.println("meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]"); pw.println(" -a: include all available information for each process."); pw.println(" -d: include dalvik details."); pw.println(" -c: dump in a compact machine-parseable representation."); + pw.println(" -s: dump only summary of application memory usage."); pw.println(" --oom: only show processes organized by oom adj."); pw.println(" --local: only collect details locally, don't call process."); pw.println(" --package: interpret process arg as package, dumping all"); @@ -14212,7 +14296,7 @@ public final class ActivityManagerService extends ActivityManagerNative mi.dalvikPrivateDirty = (int)tmpLong[0]; } ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, pid, r.baseName, 0, 0, 0, 0, 0, 0); + dumpDalvik, dumpSummaryOnly, pid, r.baseName, 0, 0, 0, 0, 0, 0); if (isCheckinRequest) { pw.println(); } @@ -14278,7 +14362,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpDetails) { if (localOnly) { ActivityThread.dumpMemInfoTable(pw, mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, pid, r.processName, 0, 0, 0, 0, 0, 0); + dumpDalvik, dumpSummaryOnly, pid, r.processName, 0, 0, 0, 0, 0, 0); if (isCheckinRequest) { pw.println(); } @@ -14286,7 +14370,7 @@ public final class ActivityManagerService extends ActivityManagerNative try { pw.flush(); thread.dumpMemInfo(fd, mi, isCheckinRequest, dumpFullDetails, - dumpDalvik, innerArgs); + dumpDalvik, dumpSummaryOnly, innerArgs); } catch (RemoteException e) { if (!isCheckinRequest) { pw.println("Got RemoteException!"); @@ -16071,7 +16155,9 @@ public final class ActivityManagerService extends ActivityManagerNative mBatteryStatsService.notePackageUninstalled(ssp); } } else { - removeTasksByRemovedPackageComponentsLocked(ssp, userId); + cleanupDisabledPackageComponentsLocked(ssp, userId, + intent.getStringArrayExtra( + Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST)); if (userId == UserHandle.USER_OWNER) { mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp); } @@ -16089,9 +16175,6 @@ public final class ActivityManagerService extends ActivityManagerNative intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); mCompatModePackages.handlePackageAddedLocked(ssp, replacing); - if (replacing) { - removeTasksByRemovedPackageComponentsLocked(ssp, userId); - } if (userId == UserHandle.USER_OWNER) { mTaskPersister.addOtherDeviceTasksToRecentsLocked(ssp); } @@ -19868,6 +19951,14 @@ public final class ActivityManagerService extends ActivityManagerNative return token; } } + + @Override + public ComponentName getHomeActivityForUser(int userId) { + synchronized (ActivityManagerService.this) { + ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId); + return homeActivity == null ? null : homeActivity.realActivity; + } + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 62d70d2..a86df2d 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -81,6 +81,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.Set; /** * State and management of a single stack of activities. @@ -4000,7 +4001,8 @@ final class ActivityStack { } } - boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) { + boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses, + boolean doit, boolean evenPersistent, int userId) { boolean didSomething = false; TaskRecord lastTask = null; ComponentName homeActivity = null; @@ -4009,10 +4011,12 @@ final class ActivityStack { int numActivities = activities.size(); for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { ActivityRecord r = activities.get(activityNdx); - final boolean samePackage = r.packageName.equals(name) - || (name == null && r.userId == userId); + final boolean sameComponent = + (r.packageName.equals(packageName) && (filterByClasses == null + || filterByClasses.contains(r.realActivity.getClassName()))) + || (packageName == null && r.userId == userId); if ((userId == UserHandle.USER_ALL || r.userId == userId) - && (samePackage || r.task == lastTask) + && (sameComponent || r.task == lastTask) && (r.app == null || evenPersistent || !r.app.persistent)) { if (!doit) { if (r.finishing) { @@ -4032,7 +4036,7 @@ final class ActivityStack { } didSomething = true; Slog.i(TAG, " Force finishing activity " + r); - if (samePackage) { + if (sameComponent) { if (r.app != null) { r.app.removed = true; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8c98f9f..cb5ba8e 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -118,6 +118,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Set; public final class ActivityStackSupervisor implements DisplayListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM; @@ -2507,14 +2508,16 @@ public final class ActivityStackSupervisor implements DisplayListener { /** * @return true if some activity was finished (or would have finished if doit were true). */ - boolean forceStopPackageLocked(String name, boolean doit, boolean evenPersistent, int userId) { + boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses, + boolean doit, boolean evenPersistent, int userId) { boolean didSomething = false; for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; final int numStacks = stacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - if (stack.forceStopPackageLocked(name, doit, evenPersistent, userId)) { + if (stack.finishDisabledPackageActivitiesLocked( + packageName, filterByClasses, doit, evenPersistent, userId)) { didSomething = true; } } @@ -2653,6 +2656,10 @@ public final class ActivityStackSupervisor implements DisplayListener { } ActivityRecord getHomeActivity() { + return getHomeActivityForUser(UserHandle.USER_ALL); + } + + ActivityRecord getHomeActivityForUser(int userId) { final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = tasks.get(taskNdx); @@ -2660,7 +2667,8 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (r.isHomeActivity()) { + if (r.isHomeActivity() + && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) { return r; } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index e5c5dff..e89ef57 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -85,7 +85,7 @@ public final class BroadcastQueue { * a bunch of processes to execute IntentReceiver components. Background- * and foreground-priority broadcasts are queued separately. */ - final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<BroadcastRecord>(); + final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>(); /** * List of all active broadcasts that are to be executed one at a time. @@ -94,7 +94,7 @@ public final class BroadcastQueue { * broadcasts, separate background- and foreground-priority queues are * maintained. */ - final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<BroadcastRecord>(); + final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>(); /** * Historical data of past broadcasts, for debugging. This is a ring buffer diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java index 7da8c48..a1dc3e3 100644 --- a/services/core/java/com/android/server/am/ProviderMap.java +++ b/services/core/java/com/android/server/am/ProviderMap.java @@ -31,6 +31,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.Set; /** * Keeps track of content providers by authority (name) and class. It separates the mapping by @@ -184,13 +185,17 @@ public final class ProviderMap { } } - private boolean collectForceStopProvidersLocked(String name, int appId, - boolean doit, boolean evenPersistent, int userId, + private boolean collectPackageProvidersLocked(String packageName, + Set<String> filterByClasses, boolean doit, boolean evenPersistent, HashMap<ComponentName, ContentProviderRecord> providers, ArrayList<ContentProviderRecord> result) { boolean didSomething = false; for (ContentProviderRecord provider : providers.values()) { - if ((name == null || provider.info.packageName.equals(name)) + final boolean sameComponent = packageName == null + || (provider.info.packageName.equals(packageName) + && (filterByClasses == null + || filterByClasses.contains(provider.name.getClassName()))); + if (sameComponent && (provider.proc == null || evenPersistent || !provider.proc.persistent)) { if (!doit) { return true; @@ -202,18 +207,18 @@ public final class ProviderMap { return didSomething; } - boolean collectForceStopProviders(String name, int appId, + boolean collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result) { - boolean didSomething = collectForceStopProvidersLocked(name, appId, doit, - evenPersistent, userId, mSingletonByClass, result); + boolean didSomething = collectPackageProvidersLocked(packageName, filterByClasses, + doit, evenPersistent, mSingletonByClass, result); if (!doit && didSomething) { return true; } if (userId == UserHandle.USER_ALL) { - for (int i=0; i<mProvidersByClassPerUser.size(); i++) { - if (collectForceStopProvidersLocked(name, appId, doit, evenPersistent, - userId, mProvidersByClassPerUser.valueAt(i), result)) { + for (int i = 0; i < mProvidersByClassPerUser.size(); i++) { + if (collectPackageProvidersLocked(packageName, filterByClasses, + doit, evenPersistent, mProvidersByClassPerUser.valueAt(i), result)) { if (!doit) { return true; } @@ -224,8 +229,8 @@ public final class ProviderMap { HashMap<ComponentName, ContentProviderRecord> items = getProvidersByClass(userId); if (items != null) { - didSomething |= collectForceStopProvidersLocked(name, appId, doit, - evenPersistent, userId, items, result); + didSomething |= collectPackageProvidersLocked(packageName, filterByClasses, + doit, evenPersistent, items, result); } } return didSomething; diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 06fba34..2149b7a 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -20,9 +20,13 @@ import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; +import static android.os.Process.FIRST_APPLICATION_UID; +import android.Manifest; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.bluetooth.BluetoothA2dp; @@ -37,7 +41,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -82,11 +89,13 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; import android.telecom.TelecomManager; import android.text.TextUtils; +import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -102,6 +111,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerService; import org.xmlpull.v1.XmlPullParserException; @@ -645,6 +655,8 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_SCREEN_ON); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); + intentFilter.addAction(Intent.ACTION_USER_BACKGROUND); + intentFilter.addAction(Intent.ACTION_USER_FOREGROUND); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); @@ -668,7 +680,7 @@ public class AudioService extends IAudioService.Stub { setRotationForAudioSystem(); } - context.registerReceiver(mReceiver, intentFilter); + context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); } @@ -4975,10 +4987,62 @@ public class AudioService extends IAudioService.Stub { 0, 0, mStreamStates[AudioSystem.STREAM_MUSIC], 0); + } else if (action.equals(Intent.ACTION_USER_BACKGROUND)) { + // Disable audio recording for the background user/profile + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userId >= 0) { + // TODO Kill recording streams instead of killing processes holding permission + UserInfo userInfo = UserManagerService.getInstance().getUserInfo(userId); + killBackgroundUserProcessesWithRecordAudioPermission(userInfo); + } + UserManagerService.getInstance().setSystemControlledUserRestriction( + UserManager.DISALLOW_RECORD_AUDIO, true, userId); + } else if (action.equals(Intent.ACTION_USER_FOREGROUND)) { + // Enable audio recording for foreground user/profile + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + UserManagerService.getInstance().setSystemControlledUserRestriction( + UserManager.DISALLOW_RECORD_AUDIO, false, userId); } } } // end class AudioServiceBroadcastReceiver + private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) { + PackageManager pm = mContext.getPackageManager(); + // Find the home activity of the user. It should not be killed to avoid expensive restart, + // when the user switches back. For managed profiles, we should kill all recording apps + ComponentName homeActivityName = null; + if (!oldUser.isManagedProfile()) { + homeActivityName = LocalServices.getService(ActivityManagerInternal.class) + .getHomeActivityForUser(oldUser.id); + } + final String[] permissions = { Manifest.permission.RECORD_AUDIO }; + List<PackageInfo> packages; + try { + packages = AppGlobals.getPackageManager() + .getPackagesHoldingPermissions(permissions, 0, oldUser.id).getList(); + } catch (RemoteException e) { + throw new AndroidRuntimeException(e); + } + for (int j = packages.size() - 1; j >= 0; j--) { + PackageInfo pkg = packages.get(j); + // Skip system processes + if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) { + continue; + } + if (homeActivityName != null + && pkg.packageName.equals(homeActivityName.getPackageName()) + && pkg.applicationInfo.isSystemApp()) { + continue; + } + try { + ActivityManagerNative.getDefault().killUid(pkg.applicationInfo.uid, + "killBackgroundUserProcessesWithAudioRecordPermission"); + } catch (RemoteException e) { + Log.w(TAG, "Error calling killUid", e); + } + } + } + //========================================================================================== // RemoteControlDisplay / RemoteControlClient / Remote info //========================================================================================== diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index ac55292..aeecdf3 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -217,14 +217,21 @@ public class Vpn { * @return true if the operation is succeeded. */ public synchronized boolean prepare(String oldPackage, String newPackage) { - if (oldPackage != null && getAppUid(oldPackage, mUserHandle) != mOwnerUID) { - // The package doesn't match. We return false (to obtain user consent) unless the user - // has already consented to that VPN package. - if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) { - prepareInternal(oldPackage); - return true; + if (oldPackage != null) { + if (getAppUid(oldPackage, mUserHandle) != mOwnerUID) { + // The package doesn't match. We return false (to obtain user consent) unless the + // user has already consented to that VPN package. + if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) { + prepareInternal(oldPackage); + return true; + } + return false; + } else if (!oldPackage.equals(VpnConfig.LEGACY_VPN) + && !isVpnUserPreConsented(oldPackage)) { + // Currently prepared VPN is revoked, so unprepare it and return false. + prepareInternal(VpnConfig.LEGACY_VPN); + return false; } - return false; } // Return true if we do not need to revoke. @@ -481,6 +488,10 @@ public class Vpn { if (Binder.getCallingUid() != mOwnerUID) { return null; } + // Check to ensure consent hasn't been revoked since we were prepared. + if (!isVpnUserPreConsented(mPackage)) { + return null; + } // Check if the service is properly declared. Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE); intent.setClassName(mPackage, config.user); diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 6f59b54..7f961ae 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -82,7 +82,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { appToken.linkToDeath(device, 0); } catch (RemoteException ex) { mVirtualDisplayDevices.remove(appToken); - device.destroyLocked(); + device.destroyLocked(false); return null; } @@ -110,7 +110,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken); if (device != null) { - device.destroyLocked(); + device.destroyLocked(true); appToken.unlinkToDeath(device, 0); } @@ -147,7 +147,7 @@ final class VirtualDisplayAdapter extends DisplayAdapter { if (device != null) { Slog.i(TAG, "Virtual display device released because application token died: " + device.mOwnerPackageName); - device.destroyLocked(); + device.destroyLocked(false); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); } } @@ -205,19 +205,19 @@ final class VirtualDisplayAdapter extends DisplayAdapter { @Override public void binderDied() { synchronized (getSyncRoot()) { - if (mSurface != null) { - handleBinderDiedLocked(mAppToken); - } + handleBinderDiedLocked(mAppToken); } } - public void destroyLocked() { + public void destroyLocked(boolean binderAlive) { if (mSurface != null) { mSurface.release(); mSurface = null; } SurfaceControl.destroyDisplay(getDisplayTokenLocked()); - mCallback.dispatchDisplayStopped(); + if (binderAlive) { + mCallback.dispatchDisplayStopped(); + } } @Override diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index e650456..51ba32d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -906,14 +906,22 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { @ServiceThreadOnly private void updateArcFeatureStatus(int portId, boolean isConnected) { assertRunOnServiceThread(); + HdmiPortInfo portInfo = mService.getPortInfo(portId); + if (!portInfo.isArcSupported()) { + return; + } HdmiDeviceInfo avr = getAvrDeviceInfo(); if (avr == null) { + if (isConnected) { + // Update the status (since TV may not have seen AVR yet) so + // that ARC can be initiated after discovery. + mArcFeatureEnabled.put(portId, isConnected); + } return; } // HEAC 2.4, HEACT 5-15 // Should not activate ARC if +5V status is false. - HdmiPortInfo portInfo = mService.getPortInfo(portId); - if (avr.getPortId() == portId && portInfo.isArcSupported()) { + if (avr.getPortId() == portId) { changeArcFeatureEnabled(portId, isConnected); } } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 65949bf..8086461 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -765,8 +765,9 @@ public class MediaSessionService extends SystemService implements Monitor { // If we don't have a media button receiver to fall back on // include non-playing sessions for dispatching UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser()); - boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null - && ur.mRestoredMediaButtonReceiver == null; + boolean useNotPlayingSessions = (ur == null) || + (ur.mLastMediaButtonReceiver == null + && ur.mRestoredMediaButtonReceiver == null); MediaSessionRecord session = mPriorityStack .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions); if (isVoiceKey(keyEvent.getKeyCode())) { diff --git a/services/core/java/com/android/server/notification/CalendarTracker.java b/services/core/java/com/android/server/notification/CalendarTracker.java index 28da73c..71d7f19 100644 --- a/services/core/java/com/android/server/notification/CalendarTracker.java +++ b/services/core/java/com/android/server/notification/CalendarTracker.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.service.notification.ZenModeConfig.EventInfo.ANY_CALENDAR; + import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; @@ -150,7 +152,7 @@ public class CalendarTracker { eventId, owner, calendarId)); final boolean meetsTime = time >= begin && time < end; final boolean meetsCalendar = visible - && (filter.calendar == 0 || filter.calendar == calendarId) + && (filter.calendar == ANY_CALENDAR || filter.calendar == calendarId) && availability != Instances.AVAILABILITY_FREE; if (meetsCalendar) { if (DEBUG) Log.d(TAG, " MEETS CALENDAR"); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index b92c734..6f8e3ca 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -189,6 +189,10 @@ abstract public class ManagedServices { } } + public boolean isComponentEnabledForPackage(String pkg) { + return mEnabledServicesPackageNames.contains(pkg); + } + public void onPackagesChanged(boolean queryReplace, String[] pkgList) { if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)) diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 25998da..791c1de 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -37,7 +37,6 @@ import android.app.NotificationManager.Policy; import android.app.PendingIntent; import android.app.StatusBarManager; import android.app.usage.UsageEvents; -import android.app.usage.UsageStats; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -233,7 +232,7 @@ public class NotificationManagerService extends SystemService { new ArrayMap<String, NotificationRecord>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); - private final ArrayMap<String, Policy.Token> mPolicyTokens = new ArrayMap<>(); + private final ArrayMap<String, Boolean> mPolicyAccess = new ArrayMap<>(); // The last key in this list owns the hardware. @@ -899,6 +898,7 @@ public class NotificationManagerService extends SystemService { @Override void onZenModeChanged() { + sendRegisteredOnlyBroadcast(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); synchronized(mNotificationList) { updateInterruptionFilterLocked(); } @@ -906,9 +906,12 @@ public class NotificationManagerService extends SystemService { @Override void onPolicyChanged() { - getContext().sendBroadcast( - new Intent(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); + sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); + } + + private void sendRegisteredOnlyBroadcast(String action) { + getContext().sendBroadcast(new Intent(action) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); } }); final File systemDir = new File(Environment.getDataDirectory(), "system"); @@ -1607,6 +1610,19 @@ public class NotificationManagerService extends SystemService { } @Override + public void setInterruptionFilter(String pkg, int filter) throws RemoteException { + enforcePolicyAccess(pkg, "setInterruptionFilter"); + final int zen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); + if (zen == -1) throw new IllegalArgumentException("Invalid filter: " + filter); + final long identity = Binder.clearCallingIdentity(); + try { + mZenModeHelper.setManualZenMode(zen, null, "setInterruptionFilter"); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void notifyConditions(String pkg, IConditionProvider provider, Condition[] conditions) { final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider); @@ -1641,16 +1657,19 @@ public class NotificationManagerService extends SystemService { message); } - private void enforcePolicyToken(Policy.Token token, String method) { - if (!checkPolicyToken(token)) { - Slog.w(TAG, "Invalid notification policy token calling " + method); - throw new SecurityException("Invalid notification policy token"); + private void enforcePolicyAccess(String pkg, String method) { + if (!checkPolicyAccess(pkg)) { + Slog.w(TAG, "Notification policy access denied calling " + method); + throw new SecurityException("Notification policy access denied"); } } - private boolean checkPolicyToken(Policy.Token token) { - return mPolicyTokens.containsValue(token) - || mListeners.mPolicyTokens.containsValue(token); + private boolean checkPackagePolicyAccess(String pkg) { + return Boolean.TRUE.equals(mPolicyAccess.get(pkg)); + } + + private boolean checkPolicyAccess(String pkg) { + return checkPackagePolicyAccess(pkg) || mListeners.isComponentEnabledForPackage(pkg); } @Override @@ -1702,52 +1721,76 @@ public class NotificationManagerService extends SystemService { } @Override - public Policy.Token getPolicyTokenFromListener(INotificationListener listener) { + public void requestNotificationPolicyAccess(String pkg, + INotificationManagerCallback callback) throws RemoteException { + if (callback == null) { + Slog.w(TAG, "requestNotificationPolicyAccess: no callback specified"); + return; + } + if (pkg == null) { + Slog.w(TAG, "requestNotificationPolicyAccess denied: no package specified"); + callback.onPolicyRequestResult(false); + return; + } final long identity = Binder.clearCallingIdentity(); try { - return mListeners.getPolicyToken(listener); + synchronized (mNotificationList) { + // immediately grant for now + mPolicyAccess.put(pkg, true); + if (DBG) Slog.w(TAG, "requestNotificationPolicyAccess granted for " + pkg); + } } finally { Binder.restoreCallingIdentity(identity); } + callback.onPolicyRequestResult(true); } @Override - public void requestNotificationPolicyToken(String pkg, - INotificationManagerCallback callback) throws RemoteException { - if (callback == null) { - Slog.w(TAG, "requestNotificationPolicyToken: no callback specified"); - return; - } - if (pkg == null) { - Slog.w(TAG, "requestNotificationPolicyToken denied: no package specified"); - callback.onPolicyToken(null); - return; - } - Policy.Token token = null; + public boolean isNotificationPolicyAccessGranted(String pkg) { + return checkPolicyAccess(pkg); + } + + @Override + public boolean isNotificationPolicyAccessGrantedForPackage(String pkg) { + enforceSystemOrSystemUI("request policy access status for another package"); + return checkPackagePolicyAccess(pkg); + } + + @Override + public String[] getPackagesRequestingNotificationPolicyAccess() + throws RemoteException { + enforceSystemOrSystemUI("request policy access packages"); final long identity = Binder.clearCallingIdentity(); try { synchronized (mNotificationList) { - token = mPolicyTokens.get(pkg); - if (token == null) { - token = new Policy.Token(new Binder()); - mPolicyTokens.put(pkg, token); + final String[] rt = new String[mPolicyAccess.size()]; + for (int i = 0; i < mPolicyAccess.size(); i++) { + rt[i] = mPolicyAccess.keyAt(i); } - if (DBG) Slog.w(TAG, "requestNotificationPolicyToken granted for " + pkg); + return rt; } } finally { Binder.restoreCallingIdentity(identity); } - callback.onPolicyToken(token); } @Override - public boolean isNotificationPolicyTokenValid(String pkg, Policy.Token token) { - return checkPolicyToken(token); + public void setNotificationPolicyAccessGranted(String pkg, boolean granted) + throws RemoteException { + enforceSystemOrSystemUI("grant notification policy access"); + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mNotificationList) { + mPolicyAccess.put(pkg, granted); + } + } finally { + Binder.restoreCallingIdentity(identity); + } } @Override - public Policy getNotificationPolicy(Policy.Token token) { - enforcePolicyToken(token, "getNotificationPolicy"); + public Policy getNotificationPolicy(String pkg) { + enforcePolicyAccess(pkg, "getNotificationPolicy"); final long identity = Binder.clearCallingIdentity(); try { return mZenModeHelper.getNotificationPolicy(); @@ -1757,8 +1800,8 @@ public class NotificationManagerService extends SystemService { } @Override - public void setNotificationPolicy(Policy.Token token, Policy policy) { - enforcePolicyToken(token, "setNotificationPolicy"); + public void setNotificationPolicy(String pkg, Policy policy) { + enforcePolicyAccess(pkg, "setNotificationPolicy"); final long identity = Binder.clearCallingIdentity(); try { mZenModeHelper.setNotificationPolicy(policy); @@ -1881,11 +1924,9 @@ public class NotificationManagerService extends SystemService { pw.print(listener.component); } pw.println(')'); - pw.print(" mPolicyTokens.keys: "); - pw.println(TextUtils.join(",", mPolicyTokens.keySet())); - pw.print(" mListeners.mPolicyTokens.keys: "); - pw.println(TextUtils.join(",", mListeners.mPolicyTokens.keySet())); } + pw.println("\n Policy access:"); + pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess); pw.println("\n Condition providers:"); mConditionProviders.dump(pw, filter); @@ -3138,18 +3179,12 @@ public class NotificationManagerService extends SystemService { public class NotificationListeners extends ManagedServices { private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>(); - private final ArrayMap<ComponentName, Policy.Token> mPolicyTokens = new ArrayMap<>(); private boolean mNotificationGroupsDesired; public NotificationListeners() { super(getContext(), mHandler, mNotificationList, mUserProfiles); } - public Policy.Token getPolicyToken(INotificationListener listener) { - final ManagedServiceInfo info = checkServiceTokenLocked(listener); - return info == null ? null : mPolicyTokens.get(info.component); - } - @Override protected Config getConfig() { Config c = new Config(); @@ -3174,7 +3209,6 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationList) { updateNotificationGroupsDesiredLocked(); update = makeRankingUpdateLocked(info); - mPolicyTokens.put(info.component, new Policy.Token(new Binder())); } try { listener.onListenerConnected(update); @@ -3191,7 +3225,6 @@ public class NotificationManagerService extends SystemService { } mLightTrimListeners.remove(removed); updateNotificationGroupsDesiredLocked(); - mPolicyTokens.remove(removed.component); } public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) { diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index e97def8..aeb6b78 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -22,6 +22,7 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import android.app.AppOpsManager; +import android.app.NotificationManager; import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.content.ContentResolver; @@ -42,6 +43,7 @@ import android.provider.Settings.Global; import android.service.notification.IConditionListener; import android.service.notification.NotificationListenerService; import android.service.notification.ZenModeConfig; +import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenModeConfig.ZenRule; import android.util.ArraySet; @@ -90,6 +92,7 @@ public class ZenModeHelper { mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mDefaultConfig = readDefaultConfig(context.getResources()); appendDefaultScheduleRules(mDefaultConfig); + appendDefaultEventRules(mDefaultConfig); mConfig = mDefaultConfig; mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); @@ -142,11 +145,11 @@ public class ZenModeHelper { } public int getZenModeListenerInterruptionFilter() { - return getZenModeListenerInterruptionFilter(mZenMode); + return NotificationManager.zenModeToInterruptionFilter(mZenMode); } - public void requestFromListener(ComponentName name, int interruptionFilter) { - final int newZen = zenModeFromListenerInterruptionFilter(interruptionFilter, -1); + public void requestFromListener(ComponentName name, int filter) { + final int newZen = NotificationManager.zenModeFromInterruptionFilter(filter, -1); if (newZen != -1) { setManualZenMode(newZen, null, "listener:" + (name != null ? name.flattenToShortString() : null)); @@ -393,37 +396,6 @@ public class ZenModeHelper { } } - private static int getZenModeListenerInterruptionFilter(int zen) { - switch (zen) { - case Global.ZEN_MODE_OFF: - return NotificationListenerService.INTERRUPTION_FILTER_ALL; - case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: - return NotificationListenerService.INTERRUPTION_FILTER_PRIORITY; - case Global.ZEN_MODE_ALARMS: - return NotificationListenerService.INTERRUPTION_FILTER_ALARMS; - case Global.ZEN_MODE_NO_INTERRUPTIONS: - return NotificationListenerService.INTERRUPTION_FILTER_NONE; - default: - return 0; - } - } - - private static int zenModeFromListenerInterruptionFilter(int listenerInterruptionFilter, - int defValue) { - switch (listenerInterruptionFilter) { - case NotificationListenerService.INTERRUPTION_FILTER_ALL: - return Global.ZEN_MODE_OFF; - case NotificationListenerService.INTERRUPTION_FILTER_PRIORITY: - return Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; - case NotificationListenerService.INTERRUPTION_FILTER_ALARMS: - return Global.ZEN_MODE_ALARMS; - case NotificationListenerService.INTERRUPTION_FILTER_NONE: - return Global.ZEN_MODE_NO_INTERRUPTIONS; - default: - return defValue; - } - } - private ZenModeConfig readDefaultConfig(Resources resources) { XmlResourceParser parser = null; try { @@ -469,6 +441,20 @@ public class ZenModeHelper { config.automaticRules.put(config.newRuleId(), rule2); } + private void appendDefaultEventRules(ZenModeConfig config) { + if (config == null) return; + + final EventInfo events = new EventInfo(); + events.calendar = EventInfo.ANY_CALENDAR; + events.reply = EventInfo.REPLY_YES_OR_MAYBE; + final ZenRule rule = new ZenRule(); + rule.enabled = false; + rule.name = mContext.getResources().getString(R.string.zen_mode_default_events_name); + rule.conditionId = ZenModeConfig.toEventConditionId(events); + rule.zenMode = Global.ZEN_MODE_ALARMS; + config.automaticRules.put(config.newRuleId(), rule); + } + private static int zenSeverity(int zen) { switch (zen) { case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return 1; @@ -511,6 +497,7 @@ public class ZenModeHelper { Log.i(TAG, "No existing V1 downtime found, generating default schedules"); appendDefaultScheduleRules(rt); } + appendDefaultEventRules(rt); return rt; } }; diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java index 30f8b37..18407c9 100644 --- a/services/core/java/com/android/server/pm/BasePermission.java +++ b/services/core/java/com/android/server/pm/BasePermission.java @@ -20,8 +20,6 @@ import android.content.pm.PackageParser; import android.content.pm.PermissionInfo; import android.os.UserHandle; -import com.android.internal.util.ArrayUtils; - final class BasePermission { final static int TYPE_NORMAL = 0; diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index a42e4e7..b505f7e 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -31,7 +31,6 @@ import java.util.ArrayList; import java.util.List; import dalvik.system.DexFile; -import dalvik.system.StaleDexCacheError; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; @@ -112,61 +111,60 @@ final class PackageDexOptimizer { } for (String path : paths) { - try { - final int dexoptNeeded; - if (forceDex) { - dexoptNeeded = DexFile.DEX2OAT_NEEDED; - } else { - dexoptNeeded = DexFile.getDexOptNeeded(path, - pkg.packageName, dexCodeInstructionSet, defer); + final int dexoptNeeded; + if (forceDex) { + dexoptNeeded = DexFile.DEX2OAT_NEEDED; + } else { + try { + dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName, + dexCodeInstructionSet, defer); + } catch (IOException ioe) { + Slog.w(TAG, "IOException reading apk: " + path, ioe); + return DEX_OPT_FAILED; } + } - if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { - // We're deciding to defer a needed dexopt. Don't bother dexopting for other - // paths and instruction sets. We'll deal with them all together when we process - // our list of deferred dexopts. - addPackageForDeferredDexopt(pkg); - return DEX_OPT_DEFERRED; - } + if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + // We're deciding to defer a needed dexopt. Don't bother dexopting for other + // paths and instruction sets. We'll deal with them all together when we process + // our list of deferred dexopts. + addPackageForDeferredDexopt(pkg); + return DEX_OPT_DEFERRED; + } - if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { - final String dexoptType; - String oatDir = null; - if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) { - dexoptType = "dex2oat"; + if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) { + final String dexoptType; + String oatDir = null; + if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) { + dexoptType = "dex2oat"; + try { oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet); - } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) { - dexoptType = "patchoat"; - } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) { - dexoptType = "self patchoat"; - } else { - throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); - } - Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" - + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet - + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable - + " oatDir = " + oatDir); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); - final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid, - !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet, - dexoptNeeded, vmSafeMode, debuggable, oatDir); - if (ret < 0) { + } catch (IOException ioe) { + Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName); return DEX_OPT_FAILED; } + } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) { + dexoptType = "patchoat"; + } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) { + dexoptType = "self patchoat"; + } else { + throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); + } + + Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg=" + + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet + + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable + + " oatDir = " + oatDir); + final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid, + !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet, + dexoptNeeded, vmSafeMode, debuggable, oatDir); + + // Dex2oat might fail due to compiler / verifier errors. We soldier on + // regardless, and attempt to interpret the app as a safety net. + if (ret == 0) { performedDexOpt = true; } - } catch (FileNotFoundException e) { - Slog.w(TAG, "Apk not found for dexopt: " + path); - return DEX_OPT_FAILED; - } catch (IOException e) { - Slog.w(TAG, "IOException reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (StaleDexCacheError e) { - Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e); - return DEX_OPT_FAILED; - } catch (Exception e) { - Slog.w(TAG, "Exception when doing dexopt : ", e); - return DEX_OPT_FAILED; } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dd5f7d4..477af72 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -206,6 +206,7 @@ import com.android.server.ServiceThread; import com.android.server.SystemConfig; import com.android.server.Watchdog; import com.android.server.pm.Settings.DatabaseVersion; +import com.android.server.pm.PermissionsState.PermissionState; import com.android.server.storage.DeviceStorageMonitorInternal; import org.xmlpull.v1.XmlPullParser; @@ -304,6 +305,8 @@ public class PackageManagerService extends IPackageManager.Stub { static final int REMOVE_CHATTY = 1<<16; + private static final int[] EMPTY_INT_ARRAY = new int[0]; + /** * Timeout (in milliseconds) after which the watchdog should declare that * our handler thread is wedged. The usual default for such things is one @@ -3139,18 +3142,26 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private static void enforceOnlySystemUpdatesPermissionPolicyFlags(int flagMask, int flagValues) { + if (((flagMask & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0 + || (flagValues & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) + && getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system can modify policy flags"); + } + } + @Override - public void grantPermission(String packageName, String name, int userId) { + public void grantRuntimePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, - "grantPermission"); + "grantRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, - "grantPermission"); + "grantRuntimePermission"); boolean gidsChanged = false; final SettingBase sb; @@ -3197,17 +3208,17 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void revokePermission(String packageName, String name, int userId) { + public void revokeRuntimePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, - "revokePermission"); + "revokeRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, - "revokePermission"); + "revokeRuntimePermission"); final SettingBase sb; @@ -3236,7 +3247,7 @@ public class PackageManagerService extends IPackageManager.Stub { return; } - // Critical, after this call all should never have the permission. + // Critical, after this call app should never have the permission. mSettings.writeRuntimePermissionsForUserLPr(userId, true); } @@ -3244,6 +3255,86 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public int getPermissionFlags(String name, String packageName, int userId) { + if (!sUserManager.exists(userId)) { + return 0; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "getPermissionFlags"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "getPermissionFlags"); + + synchronized (mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + final BasePermission bp = mSettings.mPermissions.get(name); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + name); + } + + SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + PermissionsState permissionsState = sb.getPermissionsState(); + return permissionsState.getPermissionFlags(name, userId); + } + } + + @Override + public void updatePermissionFlags(String name, String packageName, int flagMask, + int flagValues, int userId) { + if (!sUserManager.exists(userId)) { + return; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "updatePermissionFlags"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "updatePermissionFlags"); + + enforceOnlySystemUpdatesPermissionPolicyFlags(flagMask, flagValues); + + synchronized (mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + final BasePermission bp = mSettings.mPermissions.get(name); + if (bp == null) { + throw new IllegalArgumentException("Unknown permission: " + name); + } + + SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } + + PermissionsState permissionsState = sb.getPermissionsState(); + + if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) { + // Install and runtime permissions are stored in different places, + // so figure out what permission changed and persist the change. + if (permissionsState.getInstallPermissionState(name) != null) { + scheduleWriteSettingsLocked(); + } else if (permissionsState.getRuntimePermissionState(name, userId) != null) { + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + } + } + } + } + + @Override public boolean isProtectedBroadcast(String actionName) { synchronized (mPackages) { return mProtectedBroadcasts.contains(actionName); @@ -7530,8 +7621,8 @@ public class PackageManagerService extends IPackageManager.Stub { final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); - int[] upgradeUserIds = PermissionsState.USERS_NONE; - int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE; + int[] upgradeUserIds = EMPTY_INT_ARRAY; + int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY; boolean changedInstallPermission = false; @@ -7657,11 +7748,18 @@ public class PackageManagerService extends IPackageManager.Stub { // Grant previously granted runtime permissions. for (int userId : UserManagerService.getInstance().getUserIds()) { if (origPermissions.hasRuntimePermission(bp.name, userId)) { + PermissionState permissionState = origPermissions + .getRuntimePermissionState(bp.name, userId); + final int flags = permissionState.getFlags(); if (permissionsState.grantRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_FAILURE) { // If we cannot put the permission as it was, we have to write. changedRuntimePermissionUserIds = ArrayUtils.appendInt( changedRuntimePermissionUserIds, userId); + } else { + // Propagate the permission flags. + permissionsState.updatePermissionFlags(bp, userId, + flags, flags); } } } @@ -7669,13 +7767,28 @@ public class PackageManagerService extends IPackageManager.Stub { case GRANT_UPGRADE: { // Grant runtime permissions for a previously held install permission. - permissionsState.revokeInstallPermission(bp); - for (int userId : upgradeUserIds) { - if (permissionsState.grantRuntimePermission(bp, userId) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // If we granted the permission, we have to write. - changedRuntimePermissionUserIds = ArrayUtils.appendInt( - changedRuntimePermissionUserIds, userId); + PermissionState permissionState = origPermissions + .getInstallPermissionState(bp.name); + final int flags = permissionState != null ? permissionState.getFlags() : 0; + + origPermissions.revokeInstallPermission(bp); + // We will be transferring the permission flags, so clear them. + origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + + // If the permission is not to be promoted to runtime we ignore it and + // also its other flags as they are not applicable to install permissions. + if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) { + for (int userId : upgradeUserIds) { + if (permissionsState.grantRuntimePermission(bp, userId) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + // Transfer the permission flags. + permissionsState.updatePermissionFlags(bp, userId, + flags, flags); + // If we granted the permission, we have to write. + changedRuntimePermissionUserIds = ArrayUtils.appendInt( + changedRuntimePermissionUserIds, userId); + } } } } break; @@ -7692,6 +7805,9 @@ public class PackageManagerService extends IPackageManager.Stub { } else { if (permissionsState.revokeInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { + // Also drop the permission flags. + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); changedInstallPermission = true; Slog.i(TAG, "Un-granting permission " + perm + " from package " + pkg.packageName diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java index 3749957..171a50d 100644 --- a/services/core/java/com/android/server/pm/PermissionsState.java +++ b/services/core/java/com/android/server/pm/PermissionsState.java @@ -20,10 +20,13 @@ import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.SparseArray; import com.android.internal.util.ArrayUtils; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Set; /** @@ -55,15 +58,8 @@ public final class PermissionsState { /** The permission operation failed. */ public static final int PERMISSION_OPERATION_FAILURE = 3; - public static final int[] USERS_ALL = {UserHandle.USER_ALL}; - - public static final int[] USERS_NONE = {}; - private static final int[] NO_GIDS = {}; - private static final int FLAG_INSTALL_PERMISSIONS = 1 << 0; - private static final int FLAG_RUNTIME_PERMISSIONS = 1 << 1; - private ArrayMap<String, PermissionData> mPermissions; private int[] mGlobalGids = NO_GIDS; @@ -147,14 +143,16 @@ public final class PermissionsState { } /** - * Grant a runtime permission. + * Grant a runtime permission for a given device user. * * @param permission The permission to grant. + * @param userId The device user id. * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link * #PERMISSION_OPERATION_FAILURE}. */ public int grantRuntimePermission(BasePermission permission, int userId) { + enforceValidUserId(userId); if (userId == UserHandle.USER_ALL) { return PERMISSION_OPERATION_FAILURE; } @@ -162,15 +160,18 @@ public final class PermissionsState { } /** - * Revoke a runtime permission for a given device user. + * Revoke a runtime permission for a given device user. * * @param permission The permission to revoke. * @param userId The device user id. * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link * #PERMISSION_OPERATION_FAILURE}. + * + * @see android.content.pm.PackageManager.PermissionFlags */ public int revokeRuntimePermission(BasePermission permission, int userId) { + enforceValidUserId(userId); if (userId == UserHandle.USER_ALL) { return PERMISSION_OPERATION_FAILURE; } @@ -178,17 +179,6 @@ public final class PermissionsState { } /** - * Gets whether this state has a given permission, regardless if - * it is install time or runtime one. - * - * @param name The permission name. - * @return Whether this state has the permission. - */ - public boolean hasPermission(String name) { - return mPermissions != null && mPermissions.get(name) != null; - } - - /** * Gets whether this state has a given runtime permission for a * given device user id. * @@ -197,6 +187,7 @@ public final class PermissionsState { * @return Whether this state has the permission. */ public boolean hasRuntimePermission(String name, int userId) { + enforceValidUserId(userId); return !hasInstallPermission(name) && hasPermission(name, userId); } @@ -211,36 +202,6 @@ public final class PermissionsState { } /** - * Revokes a permission for all users regardless if it is an install or - * a runtime permission. - * - * @param permission The permission to revoke. - * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, - * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link - * #PERMISSION_OPERATION_FAILURE}. - */ - public int revokePermission(BasePermission permission) { - if (!hasPermission(permission.name)) { - return PERMISSION_OPERATION_FAILURE; - } - - int result = PERMISSION_OPERATION_SUCCESS; - - PermissionData permissionData = mPermissions.get(permission.name); - for (int userId : permissionData.getUserIds()) { - if (revokePermission(permission, userId) - == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; - break; - } - } - - mPermissions.remove(permission.name); - - return result; - } - - /** * Gets whether the state has a given permission for the specified * user, regardless if this is an install or a runtime permission. * @@ -256,49 +217,133 @@ public final class PermissionsState { } PermissionData permissionData = mPermissions.get(name); - return permissionData != null && permissionData.hasUserId(userId); + return permissionData != null && permissionData.isGranted(userId); } /** - * Gets all permissions regardless if they are install or runtime. + * Gets all permissions for a given device user id regardless if they + * are install time or runtime permissions. * + * @param userId The device user id. * @return The permissions or an empty set. */ - public Set<String> getPermissions() { - if (mPermissions != null) { - return mPermissions.keySet(); + public Set<String> getPermissions(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptySet(); } - return Collections.emptySet(); + Set<String> permissions = new ArraySet<>(); + + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String permission = mPermissions.keyAt(i); + + if (hasInstallPermission(permission)) { + permissions.add(permission); + } + + if (userId != UserHandle.USER_ALL) { + if (hasRuntimePermission(permission, userId)) { + permissions.add(permission); + } + } + } + + return permissions; } /** - * Gets all permissions for a given device user id regardless if they - * are install time or runtime permissions. + * Gets the state for an install permission or null if no such. * + * @param name The permission name. + * @return The permission state. + */ + public PermissionState getInstallPermissionState(String name) { + return getPermissionState(name, UserHandle.USER_ALL); + } + + /** + * Gets the state for a runtime permission or null if no such. + * + * @param name The permission name. * @param userId The device user id. - * @return The permissions or an empty set. + * @return The permission state. */ - public Set<String> getPermissions(int userId) { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS | FLAG_RUNTIME_PERMISSIONS, userId); + public PermissionState getRuntimePermissionState(String name, int userId) { + enforceValidUserId(userId); + return getPermissionState(name, userId); } /** - * Gets all runtime permissions. + * Gets all install permission states. * - * @return The permissions or an empty set. + * @return The permission states or an empty set. */ - public Set<String> getRuntimePermissions(int userId) { - return getPermissionsInternal(FLAG_RUNTIME_PERMISSIONS, userId); + public List<PermissionState> getInstallPermissionStates() { + return getPermissionStatesInternal(UserHandle.USER_ALL); } /** - * Gets all install permissions. + * Gets all runtime permission states. * - * @return The permissions or an empty set. + * @return The permission states or an empty set. + */ + public List<PermissionState> getRuntimePermissionStates(int userId) { + enforceValidUserId(userId); + return getPermissionStatesInternal(userId); + } + + /** + * Gets the flags for a permission regardless if it is install or + * runtime permission. + * + * @param name The permission name. + * @return The permission state or null if no such. */ - public Set<String> getInstallPermissions() { - return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS, UserHandle.USER_ALL); + public int getPermissionFlags(String name, int userId) { + PermissionState installPermState = getInstallPermissionState(name); + if (installPermState != null) { + return installPermState.getFlags(); + } + PermissionState runtimePermState = getRuntimePermissionState(name, userId); + if (runtimePermState != null) { + return runtimePermState.getFlags(); + } + return 0; + } + + /** + * Update the flags associated with a given permission. + * @param permission The permission whose flags to update. + * @param userId The user for which to update. + * @param flagMask Mask for which flags to change. + * @param flagValues New values for the mask flags. + * @return Whether the permission flags changed. + */ + public boolean updatePermissionFlags(BasePermission permission, int userId, + int flagMask, int flagValues) { + enforceValidUserId(userId); + + final boolean mayChangeFlags = flagValues != 0 || flagMask != 0; + + if (mPermissions == null) { + if (!mayChangeFlags) { + return false; + } + ensurePermissionData(permission); + } + + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData == null) { + if (!mayChangeFlags) { + return false; + } + permissionData = ensurePermissionData(permission); + } + + return permissionData.updateFlags(userId, flagMask, flagValues); } /** @@ -357,36 +402,37 @@ public final class PermissionsState { mPermissions = null; } - private Set<String> getPermissionsInternal(int flags, int userId) { - enforceValidUserId(userId); - + private PermissionState getPermissionState(String name, int userId) { if (mPermissions == null) { - return Collections.emptySet(); + return null; } + PermissionData permissionData = mPermissions.get(name); + if (permissionData == null) { + return null; + } + return permissionData.getPermissionState(userId); + } - if (userId == UserHandle.USER_ALL) { - flags = FLAG_INSTALL_PERMISSIONS; + private List<PermissionState> getPermissionStatesInternal(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptyList(); } - Set<String> permissions = new ArraySet<>(); + List<PermissionState> permissionStates = new ArrayList<>(); final int permissionCount = mPermissions.size(); for (int i = 0; i < permissionCount; i++) { - String permission = mPermissions.keyAt(i); + PermissionData permissionData = mPermissions.valueAt(i); - if ((flags & FLAG_INSTALL_PERMISSIONS) != 0) { - if (hasInstallPermission(permission)) { - permissions.add(permission); - } - } - if ((flags & FLAG_RUNTIME_PERMISSIONS) != 0) { - if (hasRuntimePermission(permission, userId)) { - permissions.add(permission); - } + PermissionState permissionState = permissionData.getPermissionState(userId); + if (permissionState != null) { + permissionStates.add(permissionState); } } - return permissions; + return permissionStates; } private int grantPermission(BasePermission permission, int userId) { @@ -397,17 +443,9 @@ public final class PermissionsState { final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId)); final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; - if (mPermissions == null) { - mPermissions = new ArrayMap<>(); - } - - PermissionData permissionData = mPermissions.get(permission.name); - if (permissionData == null) { - permissionData = new PermissionData(permission); - mPermissions.put(permission.name, permissionData); - } + PermissionData permissionData = ensurePermissionData(permission); - if (!permissionData.addUserId(userId)) { + if (!permissionData.grant(userId)) { return PERMISSION_OPERATION_FAILURE; } @@ -431,16 +469,12 @@ public final class PermissionsState { PermissionData permissionData = mPermissions.get(permission.name); - if (!permissionData.removeUserId(userId)) { + if (!permissionData.revoke(userId)) { return PERMISSION_OPERATION_FAILURE; } - if (permissionData.getUserIds() == USERS_NONE) { - mPermissions.remove(permission.name); - } - - if (mPermissions.isEmpty()) { - mPermissions = null; + if (permissionData.isDefault()) { + ensureNoPermissionData(permission.name); } if (hasGids) { @@ -468,9 +502,31 @@ public final class PermissionsState { } } + private PermissionData ensurePermissionData(BasePermission permission) { + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData == null) { + permissionData = new PermissionData(permission); + mPermissions.put(permission.name, permissionData); + } + return permissionData; + } + + private void ensureNoPermissionData(String name) { + if (mPermissions == null) { + return; + } + mPermissions.remove(name); + if (mPermissions.isEmpty()) { + mPermissions = null; + } + } + private static final class PermissionData { private final BasePermission mPerm; - private int[] mUserIds = USERS_NONE; + private SparseArray<PermissionState> mUserStates = new SparseArray<>(); public PermissionData(BasePermission perm) { mPerm = perm; @@ -478,11 +534,11 @@ public final class PermissionsState { public PermissionData(PermissionData other) { this(other.mPerm); - - if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) { - mUserIds = other.mUserIds; - } else { - mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length); + final int otherStateCount = other.mUserStates.size(); + for (int i = 0; i < otherStateCount; i++) { + final int otherUserId = other.mUserStates.keyAt(i); + PermissionState otherState = other.mUserStates.valueAt(i); + mUserStates.put(otherUserId, new PermissionState(otherState)); } } @@ -490,53 +546,146 @@ public final class PermissionsState { return mPerm.computeGids(userId); } - public int[] getUserIds() { - return mUserIds; - } - - public boolean hasUserId(int userId) { - if (mUserIds == USERS_ALL) { - return true; + public boolean isGranted(int userId) { + if (isInstallPermission()) { + userId = UserHandle.USER_ALL; } - if (userId != UserHandle.USER_ALL) { - return ArrayUtils.contains(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + if (userState == null) { + return false; } - return false; + return userState.mGranted; } - public boolean addUserId(int userId) { - if (hasUserId(userId)) { + public boolean grant(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (userId == UserHandle.USER_ALL) { - mUserIds = USERS_ALL; - return true; + if (isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.appendInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + if (userState == null) { + userState = new PermissionState(mPerm.name); + mUserStates.put(userId, userState); + } + + userState.mGranted = true; return true; } - public boolean removeUserId(int userId) { - if (!hasUserId(userId)) { + public boolean revoke(int userId) { + if (!isCompatibleUserId(userId)) { return false; } - if (mUserIds == USERS_ALL) { - mUserIds = UserManagerService.getInstance().getUserIds(); + if (!isGranted(userId)) { + return false; } - mUserIds = ArrayUtils.removeInt(mUserIds, userId); + PermissionState userState = mUserStates.get(userId); + userState.mGranted = false; - if (mUserIds.length == 0) { - mUserIds = USERS_NONE; + if (userState.isDefault()) { + mUserStates.remove(userId); } return true; } + + public PermissionState getPermissionState(int userId) { + return mUserStates.get(userId); + } + + public int getFlags(int userId) { + PermissionState userState = mUserStates.get(userId); + if (userState != null) { + return userState.mFlags; + } + return 0; + } + + public boolean isDefault() { + return mUserStates.size() <= 0; + } + + public static boolean isInstallPermissionKey(int userId) { + return userId == UserHandle.USER_ALL; + } + + public boolean updateFlags(int userId, int flagMask, int flagValues) { + if (isInstallPermission()) { + userId = UserHandle.USER_ALL; + } + + if (!isCompatibleUserId(userId)) { + return false; + } + + final int newFlags = flagValues & flagMask; + + PermissionState userState = mUserStates.get(userId); + if (userState != null) { + final int oldFlags = userState.mFlags; + userState.mFlags = (userState.mFlags & ~flagMask) | newFlags; + if (userState.isDefault()) { + mUserStates.remove(userId); + } + return userState.mFlags != oldFlags; + } else if (newFlags != 0) { + userState = new PermissionState(mPerm.name); + userState.mFlags = newFlags; + mUserStates.put(userId, userState); + return true; + } + + return false; + } + + private boolean isCompatibleUserId(int userId) { + return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId)); + } + + private boolean isInstallPermission() { + return mUserStates.size() == 1 + && mUserStates.get(UserHandle.USER_ALL) != null; + } + } + + public static final class PermissionState { + private final String mName; + private boolean mGranted; + private int mFlags; + + public PermissionState(String name) { + mName = name; + } + + public PermissionState(PermissionState other) { + mName = other.mName; + mGranted = other.mGranted; + mFlags = other.mFlags; + } + + public boolean isDefault() { + return !mGranted && mFlags == 0; + } + + public String getName() { + return mName; + } + + public boolean isGranted() { + return mGranted; + } + + public int getFlags() { + return mFlags; + } } } diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index 0c7f79d..c35258a 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -21,11 +21,13 @@ import android.content.pm.ApplicationInfo; import java.util.Arrays; abstract class SettingBase { + private static final int[] USERS_NONE = new int[0]; + int pkgFlags; int pkgPrivateFlags; protected final PermissionsState mPermissionsState; - private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE; + private int[] mPermissionsUpdatedForUserIds = USERS_NONE; SettingBase(int pkgFlags, int pkgPrivateFlags) { setFlags(pkgFlags); @@ -53,7 +55,7 @@ abstract class SettingBase { return; } - if (userIds == PermissionsState.USERS_NONE || userIds == PermissionsState.USERS_ALL) { + if (userIds == USERS_NONE) { mPermissionsUpdatedForUserIds = userIds; } else { mPermissionsUpdatedForUserIds = Arrays.copyOf(userIds, userIds.length); diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index fd70ce1..2e9656a 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -60,6 +60,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.backup.PreferredActivityBackupHelper; import com.android.server.pm.PackageManagerService.DumpState; +import com.android.server.pm.PermissionsState.PermissionState; import java.io.FileNotFoundException; import java.util.Collection; @@ -178,6 +179,8 @@ final class Settings { private static final String ATTR_CODE = "code"; private static final String ATTR_NOT_LAUNCHED = "nl"; private static final String ATTR_ENABLED = "enabled"; + private static final String ATTR_GRANTED = "granted"; + private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ENABLED_CALLER = "enabledCaller"; private static final String ATTR_STOPPED = "stopped"; // Legacy, here for reading older versions of the package-restrictions. @@ -820,14 +823,20 @@ final class Settings { } if (!used) { + PermissionsState permissionsState = sus.getPermissionsState(); + // Try to revoke as an install permission which is for all users. - if (sus.getPermissionsState().revokeInstallPermission(bp) == + // The package is gone - no need to keep flags for applying policy. + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + + if (permissionsState.revokeInstallPermission(bp) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { return UserHandle.USER_ALL; } // Try to revoke as an install permission which is per user. - if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) == + if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { return userId; } @@ -1724,10 +1733,32 @@ final class Settings { continue; } - if (permissionsState.grantInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Permission already added: " + name); - XmlUtils.skipCurrentTag(parser); + String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); + final boolean granted = grantedStr == null + || Boolean.parseBoolean(grantedStr); + + String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); + final int flags = (flagsStr != null) + ? Integer.parseInt(flagsStr, 16) : 0; + + if (granted) { + if (permissionsState.grantInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Permission already added: " + name); + XmlUtils.skipCurrentTag(parser); + } else { + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } + } else { + if (permissionsState.revokeInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Permission already added: " + name); + XmlUtils.skipCurrentTag(parser); + } else { + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } } } else { Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " @@ -1737,17 +1768,19 @@ final class Settings { } } - void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions) + void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates) throws IOException { - if (permissions.isEmpty()) { + if (permissionStates.isEmpty()) { return; } serializer.startTag(null, TAG_PERMISSIONS); - for (String permission : permissions) { + for (PermissionState permissionState : permissionStates) { serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, permission); + serializer.attribute(null, ATTR_NAME, permissionState.getName()); + serializer.attribute(null, ATTR_GRANTED, String.valueOf(permissionState.isGranted())); + serializer.attribute(null, ATTR_FLAGS, Integer.toHexString(permissionState.getFlags())); serializer.endTag(null, TAG_ITEM); } @@ -1945,7 +1978,8 @@ final class Settings { serializer.attribute(null, "userId", Integer.toString(usr.userId)); usr.signatures.writeXml(serializer, "sigs", mPastSignatures); - writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions()); + writePermissionsLPr(serializer, usr.getPermissionsState() + .getInstallPermissionStates()); serializer.endTag(null, "shared-user"); } @@ -2120,7 +2154,8 @@ final class Settings { // If this is a shared user, the permissions will be written there. if (pkg.sharedUser == null) { - writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); + writePermissionsLPr(serializer, pkg.getPermissionsState() + .getInstallPermissionStates()); } serializer.endTag(null, "updated-package"); @@ -2175,9 +2210,9 @@ final class Settings { serializer.attribute(null, "volumeUuid", pkg.volumeUuid); } pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); - if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { - writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); - } + + writePermissionsLPr(serializer, pkg.getPermissionsState() + .getInstallPermissionStates()); writeSigningKeySetLPr(serializer, pkg.keySetData); writeUpgradeKeySetsLPr(serializer, pkg.keySetData); @@ -3922,7 +3957,7 @@ final class Settings { PermissionsState permissionsState = ps.getPermissionsState(); dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id)); dumpRuntimePermissionsLPr(pw, prefix + " ", permissionsState - .getRuntimePermissions(user.id)); + .getRuntimePermissionStates(user.id)); } ArraySet<String> cmp = ps.getDisabledComponents(user.id); @@ -4071,7 +4106,8 @@ final class Settings { for (int userId : UserManagerService.getInstance().getUserIds()) { final int[] gids = permissionsState.computeGids(userId); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); + List<PermissionState> permissions = permissionsState + .getRuntimePermissionStates(userId); if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) { pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": "); dumpGidsLPr(pw, prefix + " ", gids); @@ -4120,22 +4156,29 @@ final class Settings { } } - void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) { - if (!permissions.isEmpty()) { + void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, + List<PermissionState> permissionStates) { + if (!permissionStates.isEmpty()) { pw.print(prefix); pw.println("runtime permissions:"); - for (String permission : permissions) { - pw.print(prefix); pw.print(" "); pw.println(permission); + for (PermissionState permissionState : permissionStates) { + pw.print(prefix); pw.print(" "); pw.print(permissionState.getName()); + pw.print(", granted="); pw.print(permissionState.isGranted()); + pw.print(", flags=0x"); pw.println(Integer.toHexString( + permissionState.getFlags())); } } } void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, PermissionsState permissionsState) { - Set<String> permissions = permissionsState.getInstallPermissions(); - if (!permissions.isEmpty()) { + List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates(); + if (!permissionStates.isEmpty()) { pw.print(prefix); pw.println("install permissions:"); - for (String permission : permissions) { - pw.print(prefix); pw.print(" "); pw.println(permission); + for (PermissionState permissionState : permissionStates) { + pw.print(prefix); pw.print(" "); pw.print(permissionState.getName()); + pw.print(", granted="); pw.print(permissionState.isGranted()); + pw.print(", flags=0x"); pw.println(Integer.toHexString( + permissionState.getFlags())); } } } @@ -4207,8 +4250,8 @@ final class Settings { private void writePermissionsSync(int userId) { AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId)); - ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>(); - ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>(); + ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>(); + ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>(); synchronized (mLock) { mWriteScheduled.delete(userId); @@ -4219,9 +4262,10 @@ final class Settings { PackageSetting packageSetting = mPackages.valueAt(i); if (packageSetting.sharedUser == null) { PermissionsState permissionsState = packageSetting.getPermissionsState(); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); - if (!permissions.isEmpty()) { - permissionsForPackage.put(packageName, permissions); + List<PermissionState> permissionsStates = permissionsState + .getRuntimePermissionStates(userId); + if (!permissionsStates.isEmpty()) { + permissionsForPackage.put(packageName, permissionsStates); } } } @@ -4231,9 +4275,10 @@ final class Settings { String sharedUserName = mSharedUsers.keyAt(i); SharedUserSetting sharedUser = mSharedUsers.valueAt(i); PermissionsState permissionsState = sharedUser.getPermissionsState(); - Set<String> permissions = permissionsState.getRuntimePermissions(userId); - if (!permissions.isEmpty()) { - permissionsForSharedUser.put(sharedUserName, permissions); + List<PermissionState> permissionsStates = permissionsState + .getRuntimePermissionStates(userId); + if (!permissionsStates.isEmpty()) { + permissionsForSharedUser.put(sharedUserName, permissionsStates); } } } @@ -4252,20 +4297,20 @@ final class Settings { final int packageCount = permissionsForPackage.size(); for (int i = 0; i < packageCount; i++) { String packageName = permissionsForPackage.keyAt(i); - Set<String> permissions = permissionsForPackage.valueAt(i); + List<PermissionState> permissionStates = permissionsForPackage.valueAt(i); serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, packageName); - writePermissions(serializer, permissions); + writePermissions(serializer, permissionStates); serializer.endTag(null, TAG_PACKAGE); } final int sharedUserCount = permissionsForSharedUser.size(); for (int i = 0; i < sharedUserCount; i++) { String packageName = permissionsForSharedUser.keyAt(i); - Set<String> permissions = permissionsForSharedUser.valueAt(i); + List<PermissionState> permissionStates = permissionsForSharedUser.valueAt(i); serializer.startTag(null, TAG_SHARED_USER); serializer.attribute(null, ATTR_NAME, packageName); - writePermissions(serializer, permissions); + writePermissions(serializer, permissionStates); serializer.endTag(null, TAG_SHARED_USER); } @@ -4290,20 +4335,23 @@ final class Settings { mHandler.removeMessages(userId); for (SettingBase sb : mPackages.values()) { - revokeRuntimePermissions(sb, userId); + revokeRuntimePermissionsAndClearFlags(sb, userId); } for (SettingBase sb : mSharedUsers.values()) { - revokeRuntimePermissions(sb, userId); + revokeRuntimePermissionsAndClearFlags(sb, userId); } } - private void revokeRuntimePermissions(SettingBase sb, int userId) { + private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) { PermissionsState permissionsState = sb.getPermissionsState(); - for (String permission : permissionsState.getRuntimePermissions(userId)) { - BasePermission bp = mPermissions.get(permission); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mPermissions.get(permissionState.getName()); if (bp != null) { permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); } } } @@ -4391,20 +4439,47 @@ final class Settings { continue; } - if (permissionsState.grantRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED); + final boolean granted = grantedStr == null + || Boolean.parseBoolean(grantedStr); + + String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS); + final int flags = (flagsStr != null) + ? Integer.parseInt(flagsStr, 16) : 0; + + if (granted) { + if (permissionsState.grantRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + } else { + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, flags); + + } + } else { + if (permissionsState.revokeRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + } else { + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, flags); + } } + } break; } } } - private void writePermissions(XmlSerializer serializer, Set<String> permissions) - throws IOException { - for (String permission : permissions) { + private void writePermissions(XmlSerializer serializer, + List<PermissionState> permissionStates) throws IOException { + for (PermissionState permissionState : permissionStates) { serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, permission); + serializer.attribute(null, ATTR_NAME,permissionState.getName()); + serializer.attribute(null, ATTR_GRANTED, + String.valueOf(permissionState.isGranted())); + serializer.attribute(null, ATTR_FLAGS, + Integer.toHexString(permissionState.getFlags())); serializer.endTag(null, TAG_ITEM); } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index e79a206..8dccfdf 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -50,6 +50,8 @@ import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; +import com.google.android.collect.Sets; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; @@ -68,11 +70,9 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; +import java.util.Set; import libcore.io.IoUtils; @@ -88,10 +88,6 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_ID = "id"; private static final String ATTR_CREATION_TIME = "created"; private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn"; - private static final String ATTR_SALT = "salt"; - private static final String ATTR_PIN_HASH = "pinHash"; - private static final String ATTR_FAILED_ATTEMPTS = "failedAttempts"; - private static final String ATTR_LAST_RETRY_MS = "lastAttemptMs"; private static final String ATTR_SERIAL_NO = "serialNumber"; private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber"; private static final String ATTR_PARTIAL = "partial"; @@ -129,16 +125,13 @@ public class UserManagerService extends IUserManager.Stub { private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms - // Number of attempts before jumping to the next BACKOFF_TIMES slot - private static final int BACKOFF_INC_INTERVAL = 5; - // Maximum number of managed profiles permitted is 1. This cannot be increased // without first making sure that the rest of the framework is prepared for it. private static final int MAX_MANAGED_PROFILES = 1; - // Amount of time to force the user to wait before entering the PIN again, after failing - // BACKOFF_INC_INTERVAL times. - private static final int[] BACKOFF_TIMES = { 0, 30*1000, 60*1000, 5*60*1000, 30*60*1000 }; + // Set of user restrictions, which can only be enforced by the system + private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet( + UserManager.DISALLOW_RECORD_AUDIO); static final int WRITE_USER_MSG = 1; static final int WRITE_USER_DELAY = 2*1000; // 2 seconds @@ -158,16 +151,6 @@ public class UserManagerService extends IUserManager.Stub { private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>(); private final Bundle mGuestRestrictions = new Bundle(); - class RestrictionsPinState { - long salt; - String pinHash; - int failedAttempts; - long lastAttemptTime; - } - - private final SparseArray<RestrictionsPinState> mRestrictionsPinStates = - new SparseArray<RestrictionsPinState>(); - /** * Set of user IDs being actively removed. Removed IDs linger in this set * for several seconds to work around a VFS caching issue. @@ -234,6 +217,14 @@ public class UserManagerService extends IUserManager.Stub { mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); initDefaultGuestRestrictions(); readUserListLocked(); + sInstance = this; + } + } + } + + void systemReady() { + synchronized (mInstallLock) { + synchronized (mPackagesLock) { // Prune out any partially created/partially removed users. ArrayList<UserInfo> partials = new ArrayList<UserInfo>(); for (int i = 0; i < mUsers.size(); i++) { @@ -248,12 +239,8 @@ public class UserManagerService extends IUserManager.Stub { + " (name=" + ui.name + ")"); removeUserStateLocked(ui.id); } - sInstance = this; } } - } - - void systemReady() { userForeground(UserHandle.USER_OWNER); mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); @@ -520,7 +507,7 @@ public class UserManagerService extends IUserManager.Stub { public boolean hasUserRestriction(String restrictionKey, int userId) { synchronized (mPackagesLock) { Bundle restrictions = mUserRestrictions.get(userId); - return restrictions != null ? restrictions.getBoolean(restrictionKey) : false; + return restrictions != null && restrictions.getBoolean(restrictionKey); } } @@ -535,23 +522,57 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public void setUserRestriction(String key, boolean value, int userId) { + synchronized (mPackagesLock) { + if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) { + Bundle restrictions = getUserRestrictions(userId); + restrictions.putBoolean(key, value); + setUserRestrictionsInternalLocked(restrictions, userId); + } + } + } + + @Override + public void setSystemControlledUserRestriction(String key, boolean value, int userId) { + checkSystemOrRoot("setSystemControlledUserRestriction"); + synchronized (mPackagesLock) { + Bundle restrictions = getUserRestrictions(userId); + restrictions.putBoolean(key, value); + setUserRestrictionsInternalLocked(restrictions, userId); + } + } + + @Override public void setUserRestrictions(Bundle restrictions, int userId) { checkManageUsersPermission("setUserRestrictions"); if (restrictions == null) return; synchronized (mPackagesLock) { - mUserRestrictions.get(userId).clear(); - mUserRestrictions.get(userId).putAll(restrictions); - long token = Binder.clearCallingIdentity(); - try { - mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId); - } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); - } finally { - Binder.restoreCallingIdentity(token); + final Bundle oldUserRestrictions = mUserRestrictions.get(userId); + // Restore the original state of system controlled restrictions from oldUserRestrictions + for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) { + restrictions.remove(key); + if (oldUserRestrictions.containsKey(key)) { + restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key)); + } } - scheduleWriteUserLocked(mUsers.get(userId)); + setUserRestrictionsInternalLocked(restrictions, userId); + } + } + + private void setUserRestrictionsInternalLocked(Bundle restrictions, int userId) { + final Bundle userRestrictions = mUserRestrictions.get(userId); + userRestrictions.clear(); + userRestrictions.putAll(restrictions); + long token = Binder.clearCallingIdentity(); + try { + mAppOpsService.setUserRestrictions(userRestrictions, userId); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); + } finally { + Binder.restoreCallingIdentity(token); } + scheduleWriteUserLocked(mUsers.get(userId)); } /** @@ -589,6 +610,13 @@ public class UserManagerService extends IUserManager.Stub { } } + private static void checkSystemOrRoot(String message) { + final int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID && uid != 0) { + throw new SecurityException("Only system may call: " + message); + } + } + private void writeBitmapLocked(UserInfo info, Bitmap bitmap) { try { File dir = new File(mUsersDir, Integer.toString(info.id)); @@ -806,21 +834,6 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime)); serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME, Long.toString(userInfo.lastLoggedInTime)); - RestrictionsPinState pinState = mRestrictionsPinStates.get(userInfo.id); - if (pinState != null) { - if (pinState.salt != 0) { - serializer.attribute(null, ATTR_SALT, Long.toString(pinState.salt)); - } - if (pinState.pinHash != null) { - serializer.attribute(null, ATTR_PIN_HASH, pinState.pinHash); - } - if (pinState.failedAttempts != 0) { - serializer.attribute(null, ATTR_FAILED_ATTEMPTS, - Integer.toString(pinState.failedAttempts)); - serializer.attribute(null, ATTR_LAST_RETRY_MS, - Long.toString(pinState.lastAttemptTime)); - } - } if (userInfo.iconPath != null) { serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); } @@ -940,11 +953,7 @@ public class UserManagerService extends IUserManager.Stub { String iconPath = null; long creationTime = 0L; long lastLoggedInTime = 0L; - long salt = 0L; - String pinHash = null; - int failedAttempts = 0; int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID; - long lastAttemptTime = 0L; boolean partial = false; boolean guestToRemove = false; Bundle restrictions = new Bundle(); @@ -978,10 +987,6 @@ public class UserManagerService extends IUserManager.Stub { iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0); lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0); - salt = readLongAttribute(parser, ATTR_SALT, 0L); - pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH); - failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0); - lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L); profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID, UserInfo.NO_PROFILE_GROUP_ID); String valueString = parser.getAttributeValue(null, ATTR_PARTIAL); @@ -1019,17 +1024,6 @@ public class UserManagerService extends IUserManager.Stub { userInfo.guestToRemove = guestToRemove; userInfo.profileGroupId = profileGroupId; mUserRestrictions.append(id, restrictions); - if (salt != 0L) { - RestrictionsPinState pinState = mRestrictionsPinStates.get(id); - if (pinState == null) { - pinState = new RestrictionsPinState(); - mRestrictionsPinStates.put(id, pinState); - } - pinState.salt = salt; - pinState.pinHash = pinHash; - pinState.failedAttempts = failedAttempts; - pinState.lastAttemptTime = lastAttemptTime; - } return userInfo; } catch (IOException ioe) { @@ -1431,8 +1425,6 @@ public class UserManagerService extends IUserManager.Stub { // Remove this user from the list mUsers.remove(userHandle); - - mRestrictionsPinStates.remove(userHandle); // Remove user file AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX)); userFile.delete(); @@ -1504,92 +1496,6 @@ public class UserManagerService extends IUserManager.Stub { } @Override - public boolean setRestrictionsChallenge(String newPin) { - checkManageUsersPermission("Only system can modify the restrictions pin"); - int userId = UserHandle.getCallingUserId(); - synchronized (mPackagesLock) { - RestrictionsPinState pinState = mRestrictionsPinStates.get(userId); - if (pinState == null) { - pinState = new RestrictionsPinState(); - } - if (newPin == null) { - pinState.salt = 0; - pinState.pinHash = null; - } else { - try { - pinState.salt = SecureRandom.getInstance("SHA1PRNG").nextLong(); - } catch (NoSuchAlgorithmException e) { - pinState.salt = (long) (Math.random() * Long.MAX_VALUE); - } - pinState.pinHash = passwordToHash(newPin, pinState.salt); - pinState.failedAttempts = 0; - } - mRestrictionsPinStates.put(userId, pinState); - writeUserLocked(mUsers.get(userId)); - } - return true; - } - - @Override - public int checkRestrictionsChallenge(String pin) { - checkManageUsersPermission("Only system can verify the restrictions pin"); - int userId = UserHandle.getCallingUserId(); - synchronized (mPackagesLock) { - RestrictionsPinState pinState = mRestrictionsPinStates.get(userId); - // If there's no pin set, return error code - if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) { - return UserManager.PIN_VERIFICATION_FAILED_NOT_SET; - } else if (pin == null) { - // If just checking if user can be prompted, return remaining time - int waitTime = getRemainingTimeForPinAttempt(pinState); - Slog.d(LOG_TAG, "Remaining waittime peek=" + waitTime); - return waitTime; - } else { - int waitTime = getRemainingTimeForPinAttempt(pinState); - Slog.d(LOG_TAG, "Remaining waittime=" + waitTime); - if (waitTime > 0) { - return waitTime; - } - if (passwordToHash(pin, pinState.salt).equals(pinState.pinHash)) { - pinState.failedAttempts = 0; - scheduleWriteUserLocked(mUsers.get(userId)); - return UserManager.PIN_VERIFICATION_SUCCESS; - } else { - pinState.failedAttempts++; - pinState.lastAttemptTime = System.currentTimeMillis(); - scheduleWriteUserLocked(mUsers.get(userId)); - return waitTime; - } - } - } - } - - private int getRemainingTimeForPinAttempt(RestrictionsPinState pinState) { - int backoffIndex = Math.min(pinState.failedAttempts / BACKOFF_INC_INTERVAL, - BACKOFF_TIMES.length - 1); - int backoffTime = (pinState.failedAttempts % BACKOFF_INC_INTERVAL) == 0 ? - BACKOFF_TIMES[backoffIndex] : 0; - return (int) Math.max(backoffTime + pinState.lastAttemptTime - System.currentTimeMillis(), - 0); - } - - @Override - public boolean hasRestrictionsChallenge() { - int userId = UserHandle.getCallingUserId(); - synchronized (mPackagesLock) { - return hasRestrictionsPinLocked(userId); - } - } - - private boolean hasRestrictionsPinLocked(int userId) { - RestrictionsPinState pinState = mRestrictionsPinStates.get(userId); - if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) { - return false; - } - return true; - } - - @Override public void removeRestrictions() { checkManageUsersPermission("Only system can remove restrictions"); final int userHandle = UserHandle.getCallingUserId(); @@ -1600,8 +1506,6 @@ public class UserManagerService extends IUserManager.Stub { synchronized (mPackagesLock) { // Remove all user restrictions setUserRestrictions(new Bundle(), userHandle); - // Remove restrictions pin - setRestrictionsChallenge(null); // Remove any app restrictions cleanAppRestrictions(userHandle); } @@ -1633,42 +1537,6 @@ public class UserManagerService extends IUserManager.Stub { } }); } - - /* - * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash. - * Not the most secure, but it is at least a second level of protection. First level is that - * the file is in a location only readable by the system process. - * @param password the password. - * @param salt the randomly generated salt - * @return the hash of the pattern in a String. - */ - private String passwordToHash(String password, long salt) { - if (password == null) { - return null; - } - String algo = null; - String hashed = salt + password; - try { - byte[] saltedPassword = (password + salt).getBytes(); - byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword); - byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword); - hashed = toHex(sha1) + toHex(md5); - } catch (NoSuchAlgorithmException e) { - Log.w(LOG_TAG, "Failed to encode string because of missing algorithm: " + algo); - } - return hashed; - } - - private static String toHex(byte[] ary) { - final String hex = "0123456789ABCDEF"; - String ret = ""; - for (int i = 0; i < ary.length; i++) { - ret += hex.charAt((ary[i] >> 4) & 0xf); - ret += hex.charAt(ary[i] & 0xf); - } - return ret; - } - private int getUidForPackage(String packageName) { long ident = Binder.clearCallingIdentity(); try { @@ -1954,11 +1822,6 @@ public class UserManagerService extends IUserManager.Stub { return RESTRICTIONS_FILE_PREFIX + packageName + XML_SUFFIX; } - private String restrictionsFileNameToPackage(String fileName) { - return fileName.substring(RESTRICTIONS_FILE_PREFIX.length(), - (int) (fileName.length() - XML_SUFFIX.length())); - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java index e972ec7..5877b3e 100644 --- a/services/core/java/com/android/server/policy/BarController.java +++ b/services/core/java/com/android/server/policy/BarController.java @@ -58,6 +58,9 @@ public class BarController { private int mTransientBarState; private boolean mPendingShow; private long mLastTranslucent; + private boolean mShowTransparent; + private boolean mSetUnHideFlagWhenNextTransparent; + private boolean mNoAnimationOnNextShow; public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag) { @@ -74,6 +77,14 @@ public class BarController { mWin = win; } + public void setShowTransparent(boolean transparent) { + if (transparent != mShowTransparent) { + mShowTransparent = transparent; + mSetUnHideFlagWhenNextTransparent = transparent; + mNoAnimationOnNextShow = true; + } + } + public void showTransient() { if (mWin != null) { setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED); @@ -135,7 +146,9 @@ public class BarController { } final boolean wasVis = mWin.isVisibleLw(); final boolean wasAnim = mWin.isAnimatingLw(); - final boolean change = show ? mWin.showLw(true) : mWin.hideLw(true); + final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow) + : mWin.hideLw(!mNoAnimationOnNextShow); + mNoAnimationOnNextShow = false; final int state = computeStateLw(wasVis, wasAnim, mWin, change); final boolean stateChanged = updateStateLw(state); return change || stateChanged; @@ -233,6 +246,13 @@ public class BarController { setTransientBarState(TRANSIENT_BAR_NONE); // request denied } } + if (mShowTransparent) { + vis |= View.SYSTEM_UI_TRANSPARENT; + if (mSetUnHideFlagWhenNextTransparent) { + vis |= mUnhideFlag; + mSetUnHideFlagWhenNextTransparent = false; + } + } if (mTransientBarState != TRANSIENT_BAR_NONE) { vis |= mTransientFlag; // ignore clear requests until transition completes vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index dbd3676..185ef03 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -476,6 +476,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mTopIsFullscreen; boolean mForceStatusBar; boolean mForceStatusBarFromKeyguard; + private boolean mForceStatusBarTransparent; boolean mHideLockScreen; boolean mForcingShowNavBar; int mForcingShowNavBarLayer; @@ -4129,6 +4130,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAppsThatDismissKeyguard.clear(); mForceStatusBar = false; mForceStatusBarFromKeyguard = false; + mForceStatusBarTransparent = false; mForcingShowNavBar = false; mForcingShowNavBarLayer = -1; @@ -4154,8 +4156,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { mForcingShowNavBar = true; mForcingShowNavBarLayer = win.getSurfaceLayer(); } - if (attrs.type == TYPE_STATUS_BAR && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { - mForceStatusBarFromKeyguard = true; + if (attrs.type == TYPE_STATUS_BAR) { + if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { + mForceStatusBarFromKeyguard = true; + } + if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) { + mForceStatusBarTransparent = true; + } } boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW @@ -4302,7 +4309,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar + " forcefkg=" + mForceStatusBarFromKeyguard + " top=" + mTopFullscreenOpaqueWindowState); - if (mForceStatusBar || mForceStatusBarFromKeyguard) { + boolean shouldBeTransparent = mForceStatusBarTransparent + && !mForceStatusBar + && !mForceStatusBarFromKeyguard; + if (!shouldBeTransparent) { + mStatusBarController.setShowTransparent(false /* transparent */); + } else if (!mStatusBar.isVisibleLw()) { + mStatusBarController.setShowTransparent(true /* transparent */); + } + if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent) { if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced"); if (mStatusBarController.setBarShowingLw(true)) { changes |= FINISH_LAYOUT_REDO_LAYOUT; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index f790f75..decca16 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -69,6 +69,7 @@ import android.view.WindowManagerPolicy; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import libcore.util.Objects; @@ -423,6 +424,9 @@ public final class PowerManagerService extends SystemService // True if we are currently in device idle mode. private boolean mDeviceIdleMode; + // Set of app ids that we will always respect the wake locks for. + int[] mDeviceIdleWhitelist = new int[0]; + // True if theater mode is enabled private boolean mTheaterModeEnabled; @@ -758,6 +762,7 @@ public final class PowerManagerService extends SystemService throw new IllegalArgumentException("Wake lock is already dead."); } mWakeLocks.add(wakeLock); + setWakeLockDisabledStateLocked(wakeLock); notifyAcquire = true; } @@ -894,7 +899,7 @@ public final class PowerManagerService extends SystemService } private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) { - if (mSystemReady) { + if (mSystemReady && !wakeLock.mDisabled) { wakeLock.mNotifiedAcquired = true; mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName, wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource, @@ -1388,7 +1393,10 @@ public final class PowerManagerService extends SystemService final WakeLock wakeLock = mWakeLocks.get(i); switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) { case PowerManager.PARTIAL_WAKE_LOCK: - mWakeLockSummary |= WAKE_LOCK_CPU; + if (!wakeLock.mDisabled) { + // We only respect this if the wake lock is not disabled. + mWakeLockSummary |= WAKE_LOCK_CPU; + } break; case PowerManager.FULL_WAKE_LOCK: mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT; @@ -2248,12 +2256,12 @@ public final class PowerManagerService extends SystemService } } - private void setStayOnSettingInternal(int val) { + void setStayOnSettingInternal(int val) { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, val); } - private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { + void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) { synchronized (mLock) { mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs; mDirty |= DIRTY_SETTINGS; @@ -2261,6 +2269,69 @@ public final class PowerManagerService extends SystemService } } + void setDeviceIdleModeInternal(boolean enabled) { + synchronized (mLock) { + if (mDeviceIdleMode != enabled) { + mDeviceIdleMode = enabled; + updateWakeLockDisabledStatesLocked(); + } + } + } + + void setDeviceIdleWhitelistInternal(int[] appids) { + synchronized (mLock) { + mDeviceIdleWhitelist = appids; + if (mDeviceIdleMode) { + updateWakeLockDisabledStatesLocked(); + } + } + } + + private void updateWakeLockDisabledStatesLocked() { + boolean changed = false; + final int numWakeLocks = mWakeLocks.size(); + for (int i = 0; i < numWakeLocks; i++) { + final WakeLock wakeLock = mWakeLocks.get(i); + if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) + == PowerManager.PARTIAL_WAKE_LOCK) { + if (setWakeLockDisabledStateLocked(wakeLock)) { + changed = true; + if (wakeLock.mDisabled) { + // This wake lock is no longer being respected. + notifyWakeLockReleasedLocked(wakeLock); + } else { + notifyWakeLockAcquiredLocked(wakeLock); + } + } + } + } + if (changed) { + mDirty |= DIRTY_WAKE_LOCKS; + updatePowerStateLocked(); + } + } + + private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) { + if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) + == PowerManager.PARTIAL_WAKE_LOCK) { + boolean disabled = false; + if (mDeviceIdleMode) { + final int appid = UserHandle.getAppId(wakeLock.mOwnerUid); + // If we are in idle mode, we will ignore all partial wake locks that are + // for application uids that are not whitelisted. + if (appid >= Process.FIRST_APPLICATION_UID && + Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) { + disabled = true; + } + } + if (wakeLock.mDisabled != disabled) { + wakeLock.mDisabled = disabled; + return true; + } + } + return false; + } + private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() { return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0 && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE; @@ -2459,6 +2530,8 @@ public final class PowerManagerService extends SystemService pw.println(" mSandmanSummoned=" + mSandmanSummoned); pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled); pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); + pw.println(" mDeviceIdleMode=" + mDeviceIdleMode); + pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist)); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); @@ -2671,6 +2744,7 @@ public final class PowerManagerService extends SystemService public final int mOwnerUid; public final int mOwnerPid; public boolean mNotifiedAcquired; + public boolean mDisabled; public WakeLock(IBinder lock, int flags, String tag, String packageName, WorkSource workSource, String historyTag, int ownerUid, int ownerPid) { @@ -2729,7 +2803,7 @@ public final class PowerManagerService extends SystemService @Override public String toString() { return getLockLevelString() - + " '" + mTag + "'" + getLockFlagsString() + + " '" + mTag + "'" + getLockFlagsString() + (mDisabled ? " DISABLED" : "") + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")"; } @@ -3340,9 +3414,12 @@ public final class PowerManagerService extends SystemService @Override public void setDeviceIdleMode(boolean enabled) { - synchronized (mLock) { - mDeviceIdleMode = enabled; - } + setDeviceIdleModeInternal(enabled); + } + + @Override + public void setDeviceIdleWhitelist(int[] appids) { + setDeviceIdleWhitelistInternal(appids); } } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6f01ca0..c6816b0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2897,7 +2897,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid); boolean doNotAskCredentialsOnBoot = - (flags & DevicePolicyManager.DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; + (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) { setDoNotAskCredentialsOnBoot(); } @@ -6344,10 +6344,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long ident = Binder.clearCallingIdentity(); try { + PackageManager packageManager = mContext.getPackageManager(); if (granted) { - mContext.getPackageManager().grantPermission(packageName, permission, user); + packageManager.grantRuntimePermission(packageName, permission, user); + packageManager.updatePermissionFlags(permission, packageName, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } else { - mContext.getPackageManager().revokePermission(packageName, permission, user); + packageManager.revokeRuntimePermission(packageName, + permission, user); + packageManager.updatePermissionFlags(permission, packageName, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, + PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); } return true; } catch (SecurityException se) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 117cbe4..48b127a 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -718,10 +718,10 @@ public class UsageStatsService extends SystemService implements } @Override - public boolean isAppIdle(String packageName, int userId) { + public boolean isAppInactive(String packageName, int userId) { try { userId = ActivityManagerNative.getDefault().handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, false, true, "isAppIdle", null); + Binder.getCallingUid(), userId, false, true, "isAppInactive", null); } catch (RemoteException re) { return false; } @@ -734,7 +734,7 @@ public class UsageStatsService extends SystemService implements } @Override - public void setAppIdle(String packageName, boolean idle, int userId) { + public void setAppInactive(String packageName, boolean idle, int userId) { final int callingUid = Binder.getCallingUid(); try { userId = ActivityManagerNative.getDefault().handleIncomingUser( diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 6adb8be..f84beb6 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -78,8 +78,6 @@ public class UsbDeviceManager { "/sys/class/android_usb/android0/functions"; private static final String STATE_PATH = "/sys/class/android_usb/android0/state"; - private static final String MASS_STORAGE_FILE_PATH = - "/sys/class/android_usb/android0/f_mass_storage/lun/file"; private static final String RNDIS_ETH_ADDR_PATH = "/sys/class/android_usb/android0/f_rndis/ethaddr"; private static final String AUDIO_SOURCE_PCM_PATH = @@ -832,8 +830,6 @@ public class UsbDeviceManager { + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); pw.println(" Kernel function list: " + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim()); - pw.println(" Mass storage backing file: " - + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim()); } catch (IOException e) { pw.println("IOException: " + e); } @@ -866,15 +862,6 @@ public class UsbDeviceManager { mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, makeDefault); } - public void setMassStorageBackingFile(String path) { - if (path == null) path = ""; - try { - FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path); - } catch (IOException e) { - Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH); - } - } - private void readOemUsbOverrideConfig() { String[] configList = mContext.getResources().getStringArray( com.android.internal.R.array.config_oemUsbModeOverride); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index fda8076..e03884f 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -271,16 +271,6 @@ public class UsbService extends IUsbManager.Stub { } @Override - public void setMassStorageBackingFile(String path) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - if (mDeviceManager != null) { - mDeviceManager.setMassStorageBackingFile(path); - } else { - throw new IllegalStateException("USB device mode not supported"); - } - } - - @Override public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 2897c61..fcdb6d6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -754,7 +754,7 @@ public class VoiceInteractionManagerService extends SystemService { public boolean activeServiceSupportsAssist() { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { - return mImpl != null && mImpl.mInfo.getSupportsAssist(); + return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist(); } } @@ -762,7 +762,8 @@ public class VoiceInteractionManagerService extends SystemService { public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { - return mImpl != null && mImpl.mInfo.getSupportsLaunchFromKeyguard(); + return mImpl != null && mImpl.mInfo != null + && mImpl.mInfo.getSupportsLaunchFromKeyguard(); } } |