diff options
author | Steve Kondik <steve@cyngn.com> | 2015-12-07 18:50:52 -0800 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2015-12-07 18:50:52 -0800 |
commit | 10c07f778a611d8723619f67e5709cbd9e502a07 (patch) | |
tree | 2747712ffb2d61348ec7b241d0c2a764397a07a5 /services | |
parent | 45c11b1020a64aae88b859870d5b2e312dab4f76 (diff) | |
parent | 4d70bd7a928903b35c92619437c70bc382587b71 (diff) | |
download | frameworks_base-10c07f778a611d8723619f67e5709cbd9e502a07.zip frameworks_base-10c07f778a611d8723619f67e5709cbd9e502a07.tar.gz frameworks_base-10c07f778a611d8723619f67e5709cbd9e502a07.tar.bz2 |
Merge tag 'android-6.0.1_r3' of https://android.googlesource.com/platform/frameworks/base into cm-13.0
Android 6.0.1 release 3
Change-Id: I59b9e5a943e0860d43bcfb36ee0e8b8b072412ea
Diffstat (limited to 'services')
45 files changed, 942 insertions, 325 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 48c89cf..8607a40 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -185,7 +185,7 @@ class AlarmManagerService extends SystemService { private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY; - private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; + private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000; private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000; // Minimum futurity of a new alarm @@ -1000,8 +1000,8 @@ class AlarmManagerService extends SystemService { // This is a special alarm that will put the system into idle until it goes off. // The caller has given the time they want this to happen at, however we need // to pull that earlier if there are existing alarms that have requested to - // bring us out of idle. - if (mNextWakeFromIdle != null) { + // bring us out of idle at an earlier time. + if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) { a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } // Add fuzz to make the alarm go off some time before the actual desired time. @@ -1318,7 +1318,7 @@ class AlarmManagerService extends SystemService { pw.print(" Idling until: "); if (mPendingIdleUntil != null) { pw.println(mPendingIdleUntil); - mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); + mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); } else { pw.println("null"); } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 5d0b4f8..b5bf12c 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -44,7 +44,6 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.media.AudioAttributes; import android.os.AsyncTask; import android.os.Binder; @@ -310,12 +309,12 @@ public class AppOpsService extends IAppOpsService.Stub { Iterator<Ops> it = pkgs.values().iterator(); while (it.hasNext()) { Ops ops = it.next(); - int curUid; + int curUid = -1; try { - curUid = mContext.getPackageManager().getPackageUid(ops.packageName, + curUid = AppGlobals.getPackageManager().getPackageUidEtc(ops.packageName, + PackageManager.GET_UNINSTALLED_PACKAGES, UserHandle.getUserId(ops.uidState.uid)); - } catch (NameNotFoundException e) { - curUid = -1; + } catch (RemoteException ignored) { } if (curUid != ops.uidState.uid) { Slog.i(TAG, "Pruning old package " + ops.packageName diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e46aff2..bc356da 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -953,13 +953,13 @@ public class ConnectivityService extends IConnectivityManager.Stub uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & RULE_REJECT_ALL) != 0 - || (networkCostly && (uidRules & RULE_REJECT_METERED) != 0)) { + if (uidRules == RULE_REJECT_ALL) { return true; + } else if ((uidRules == RULE_REJECT_METERED) && networkCostly) { + return true; + } else { + return false; } - - // no restrictive rules; network is visible - return false; } /** @@ -1454,10 +1454,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void enforceChangePermission() { - int uid = Binder.getCallingUid(); - Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings - .getPackageNameForUid(mContext, uid), true); - + ConnectivityManager.enforceChangePermission(mContext); } private void enforceTetherAccessPermission() { @@ -3227,6 +3224,11 @@ public class ConnectivityService extends IConnectivityManager.Stub final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN)); final VpnProfile profile = VpnProfile.decode( profileName, mKeyStore.get(Credentials.VPN + profileName)); + if (profile == null) { + Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName); + setLockdownTracker(null); + return true; + } int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized(mVpns) { setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user), @@ -3373,7 +3375,7 @@ public class ConnectivityService extends IConnectivityManager.Stub .setPriority(highPriority ? Notification.PRIORITY_HIGH : Notification.PRIORITY_DEFAULT) - .setDefaults(Notification.DEFAULT_ALL) + .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0) .setOnlyAlertOnce(true) .build(); @@ -3739,7 +3741,7 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized(mRulesLock) { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & (RULE_REJECT_METERED | RULE_REJECT_ALL)) != 0) { + if (uidRules != RULE_ALLOW_ALL) { // we could silently fail or we can filter the available nets to only give // them those they have access to. Chose the more useful networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 38adfbd..abe8f5c 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -242,6 +242,14 @@ public class DeviceIdleController extends SystemService if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { int plugged = intent.getIntExtra("plugged", 0); updateChargingLocked(plugged != 0); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + removePowerSaveWhitelistAppInternal(ssp); + } + } } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { synchronized (DeviceIdleController.this) { stepIdleStateLocked(); @@ -918,6 +926,10 @@ public class DeviceIdleController extends SystemService filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(ACTION_STEP_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + getContext().registerReceiver(mReceiver, filter); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); @@ -930,7 +942,10 @@ public class DeviceIdleController extends SystemService public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { - ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, 0); + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) { reportPowerSaveWhitelistChangedLocked(); updateWhitelistAppIdsLocked(); @@ -1271,7 +1286,6 @@ public class DeviceIdleController extends SystemService if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); EventLogTags.writeDeviceIdle(mState, "step"); scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT); - cancelSensingAlarmLocked(); cancelLocatingLocked(); mAnyMotionDetector.checkForAnyMotion(); mNotMoving = false; @@ -1283,7 +1297,6 @@ public class DeviceIdleController extends SystemService mState = STATE_LOCATING; if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); EventLogTags.writeDeviceIdle(mState, "step"); - cancelSensingAlarmLocked(); scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT); mLocating = true; mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener, @@ -1341,6 +1354,7 @@ public class DeviceIdleController extends SystemService mState = STATE_ACTIVE; mInactiveTimeout = timeout; EventLogTags.writeDeviceIdle(mState, type); + cancelSensingAlarmLocked(); becomeInactiveIfAppropriateLocked(); } } @@ -1524,7 +1538,6 @@ public class DeviceIdleController extends SystemService } catch (IOException e) { } } - } private void readConfigFileLocked(XmlPullParser parser) { @@ -1553,7 +1566,10 @@ public class DeviceIdleController extends SystemService String name = parser.getAttributeValue(null, "n"); if (name != null) { try { - ApplicationInfo ai = pm.getApplicationInfo(name, 0); + ApplicationInfo ai = pm.getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); mPowerSaveWhitelistUserApps.put(ai.packageName, UserHandle.getAppId(ai.uid)); } catch (PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 8645766..6d6ca3c 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -40,6 +40,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.annotation.Nullable; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AlertDialog; @@ -289,8 +290,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub boolean mSystemReady; /** - * Id of the currently selected input method. + * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. + * method. This is to be synchronized with the secure settings keyed with + * {@link Settings.Secure#DEFAULT_INPUT_METHOD}. + * + * <p>This can be transiently {@code null} when the system is re-initializing input method + * settings, e.g., the system locale is just changed.</p> + * + * <p>Note that {@link #mCurId} is used to track which IME is being connected to + * {@link InputMethodManagerService}.</p> + * + * @see #mCurId */ + @Nullable String mCurMethodId; /** @@ -320,9 +332,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub EditorInfo mCurAttribute; /** - * The input method ID of the input method service that we are currently + * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently * connected to or in the process of connecting to. + * + * <p>This can be {@code null} when no input method is connected.</p> + * + * @see #mCurMethodId */ + @Nullable String mCurId; /** @@ -978,7 +995,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub || (newLocale != null && !newLocale.equals(mLastSystemLocale))) { if (!updateOnlyWhenLocaleChanged) { hideCurrentInputLocked(0, null); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } if (DEBUG) { @@ -1532,7 +1548,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub channel.dispose(); } - void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { + void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) { + if (resetCurrentMethodAndClient) { + mCurMethodId = null; + } + if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -1559,9 +1579,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mCurId = null; clearCurMethodLocked(); - if (reportToClient && mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + if (resetCurrentMethodAndClient) { + unbindCurrentClientLocked(); } } @@ -1912,13 +1931,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } // code to disable the CM Phone IME switcher with config_show_cmIMESwitcher set = false diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index b135250..4847de3 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -2419,8 +2419,13 @@ class MountService extends IMountService.Stub } try { - mCryptConnector.execute("cryptfs", "enablecrypto", wipe ? "wipe" : "inplace", CRYPTO_TYPES[type], - new SensitiveArg(password)); + if (type == StorageManager.CRYPT_TYPE_DEFAULT) { + mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", + CRYPTO_TYPES[type]); + } else { + mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", + CRYPTO_TYPES[type], new SensitiveArg(password)); + } } catch (NativeDaemonConnectorException e) { // Encryption failed return e.getCode(); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 95c6c0e..fd42b0a 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2055,9 +2055,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void setFirewallChainEnabled(int chain, boolean enable) { enforceSystemUid(); synchronized (mQuotaLock) { - if (mFirewallChainStates.indexOfKey(chain) >= 0 && - mFirewallChainStates.get(chain) == enable) { - // All is the same, nothing to do. + if (mFirewallChainStates.get(chain, false) == enable) { + // All is the same, nothing to do. This relies on the fact that netd has child + // chains default detached. return; } mFirewallChainStates.put(chain, enable); diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index a0d305c..3f0664d 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -30,6 +30,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.os.PowerManager; import android.provider.Settings; import android.util.Log; import android.util.NtpTrustedTime; @@ -75,6 +76,7 @@ public class NetworkTimeUpdateService { private SettingsObserver mSettingsObserver; // The last time that we successfully fetched the NTP time. private long mLastNtpFetchTime = NOT_SET; + private final PowerManager.WakeLock mWakeLock; // Normal polling frequency private final long mPollingIntervalMs; @@ -104,6 +106,9 @@ public class NetworkTimeUpdateService { com.android.internal.R.integer.config_ntpRetry); mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); + + mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, TAG); } /** Initialize the receivers and initiate the first NTP request */ @@ -148,7 +153,15 @@ public class NetworkTimeUpdateService { private void onPollNetworkTime(int event) { // If Automatic time is not set, don't bother. if (!isAutomaticTimeRequested()) return; + mWakeLock.acquire(); + try { + onPollNetworkTimeUnderWakeLock(event); + } finally { + mWakeLock.release(); + } + } + private void onPollNetworkTimeUnderWakeLock(int event) { final long refTime = SystemClock.elapsedRealtime(); // If NITZ time was received less than mPollingIntervalMs time ago, // no need to sync to NTP. diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index edc3685..1e0cf0a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3799,7 +3799,6 @@ public class AccountManagerService boolean isPermitted = isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, Manifest.permission.GET_ACCOUNTS_PRIVILEGED); - Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted)); return getTypesForCaller(callingUid, userId, isPermitted); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 9730111..542268e 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1314,6 +1314,15 @@ public final class ActiveServices { if (!mRestartingServices.contains(r)) { return; } + if (!isServiceNeeded(r, false, false)) { + // Paranoia: is this service actually needed? In theory a service that is not + // needed should never remain on the restart list. In practice... well, there + // have been bugs where this happens, and bad things happen because the process + // ends up just being cached, so quickly killed, then restarted again and again. + // Let's not let that happen. + Slog.wtf(TAG, "Restarting service that is not needed: " + r); + return; + } try { bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true); } catch (TransactionTooLargeException e) { @@ -2043,6 +2052,13 @@ public final class ActiveServices { mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg); didSomething = true; + if (!isServiceNeeded(sr, false, false)) { + // We were waiting for this service to start, but it is actually no + // longer needed. This could happen because bringDownServiceIfNeeded + // won't bring down a service that is pending... so now the pending + // is done, so let's drop it. + bringDownServiceLocked(sr); + } } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting service " @@ -2055,7 +2071,7 @@ public final class ActiveServices { // be weird to bring up the process but arbitrarily not let the services // run at this point just because their restart time hasn't come up. if (mRestartingServices.size() > 0) { - ServiceRecord sr = null; + ServiceRecord sr; for (int i=0; i<mRestartingServices.size(); i++) { sr = mRestartingServices.get(i); if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1bf7931..b34a6ef 100755 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -381,6 +381,10 @@ public final class ActivityManagerService extends ActivityManagerNative // we will consider it to be doing interaction for usage stats. static final int SERVICE_USAGE_INTERACTION_TIME = 30*60*1000; + // Maximum amount of time we will allow to elapse before re-reporting usage stats + // interaction with foreground processes. + static final long USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L; + // Maximum number of users we allow to be running at a time. static final int MAX_RUNNING_USERS = 3; @@ -18926,7 +18930,8 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now) { + private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now, + long nowElapsed) { boolean success = true; if (app.curRawAdj != app.setRawAdj) { @@ -19042,7 +19047,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (app.setProcState != app.curProcState) { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Proc state change of " + app.processName - + " to " + app.curProcState); + + " to " + app.curProcState); boolean setImportant = app.setProcState < ActivityManager.PROCESS_STATE_SERVICE; boolean curImportant = app.curProcState < ActivityManager.PROCESS_STATE_SERVICE; if (setImportant && !curImportant) { @@ -19053,14 +19058,14 @@ public final class ActivityManagerService extends ActivityManagerNative BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (stats) { app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, - app.pid, SystemClock.elapsedRealtime()); + app.pid, nowElapsed); } app.lastCpuTime = app.curCpuTime; } // Inform UsageStats of important process state change // Must be called before updating setProcState - maybeUpdateUsageStatsLocked(app); + maybeUpdateUsageStatsLocked(app, nowElapsed); app.setProcState = app.curProcState; if (app.setProcState >= ActivityManager.PROCESS_STATE_HOME) { @@ -19071,6 +19076,11 @@ public final class ActivityManagerService extends ActivityManagerNative } else { app.procStateChanged = true; } + } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime) + > USAGE_STATS_INTERACTION_INTERVAL) { + // For apps that sit around for a long time in the interactive state, we need + // to report this at least once a day so they don't go idle. + maybeUpdateUsageStatsLocked(app, nowElapsed); } if (changes != 0) { @@ -19171,7 +19181,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private void maybeUpdateUsageStatsLocked(ProcessRecord app) { + private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) { if (DEBUG_USAGE_STATS) { Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList()) + "] state changes: old = " + app.setProcState + ", new = " @@ -19188,19 +19198,20 @@ public final class ActivityManagerService extends ActivityManagerNative isInteraction = true; app.fgInteractionTime = 0; } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) { - final long now = SystemClock.elapsedRealtime(); if (app.fgInteractionTime == 0) { - app.fgInteractionTime = now; + app.fgInteractionTime = nowElapsed; isInteraction = false; } else { - isInteraction = now > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME; + isInteraction = nowElapsed > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME; } } else { isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; app.fgInteractionTime = 0; } - if (isInteraction && !app.reportedInteraction) { + if (isInteraction && (!app.reportedInteraction + || (nowElapsed-app.interactionEventTime) > USAGE_STATS_INTERACTION_INTERVAL)) { + app.interactionEventTime = nowElapsed; String[] packages = app.getPackageList(); if (packages != null) { for (int i = 0; i < packages.length; i++) { @@ -19210,6 +19221,9 @@ public final class ActivityManagerService extends ActivityManagerNative } } app.reportedInteraction = isInteraction; + if (!isInteraction) { + app.interactionEventTime = 0; + } } private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { @@ -19232,7 +19246,7 @@ public final class ActivityManagerService extends ActivityManagerNative computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); - return applyOomAdjLocked(app, doingAll, now); + return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime()); } final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground, @@ -19324,6 +19338,7 @@ public final class ActivityManagerService extends ActivityManagerNative final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final long now = SystemClock.uptimeMillis(); + final long nowElapsed = SystemClock.elapsedRealtime(); final long oldTime = now - ProcessList.MAX_EMPTY_TIME; final int N = mLruProcesses.size(); @@ -19481,7 +19496,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - applyOomAdjLocked(app, true, now); + applyOomAdjLocked(app, true, now, nowElapsed); // Count the number of process types. switch (app.curProcState) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6ccd939..be90dc8 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2496,8 +2496,13 @@ public final class ActivityStackSupervisor implements DisplayListener { final void doPendingActivityLaunchesLocked(boolean doResume) { while (!mPendingActivityLaunches.isEmpty()) { PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); - startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags, - doResume && mPendingActivityLaunches.isEmpty(), null, null); + + try { + startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags, + doResume && mPendingActivityLaunches.isEmpty(), null, null); + } catch (Exception e) { + Slog.w(TAG, "Exception during pending activity launch pal=" + pal, e); + } } } @@ -3959,7 +3964,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskModeTasks.add(task); if (task.mLockTaskUid == -1) { - task.mLockTaskUid = task.mCallingUid; + task.mLockTaskUid = task.effectiveUid; } if (andResume) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 320d477..589a4b8 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -44,6 +44,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; +import android.util.TimeUtils; import com.android.server.DeviceIdleController; import static com.android.server.am.ActivityManagerDebugConfig.*; @@ -1297,6 +1298,7 @@ public final class BroadcastQueue { final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage, boolean needSep) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 || mPendingBroadcast != null) { boolean printed = false; @@ -1314,7 +1316,7 @@ public final class BroadcastQueue { pw.println(" Active broadcasts [" + mQueueName + "]:"); } pw.println(" Active Broadcast " + mQueueName + " #" + i + ":"); - br.dump(pw, " "); + br.dump(pw, " ", sdf); } printed = false; needSep = true; @@ -1332,7 +1334,7 @@ public final class BroadcastQueue { pw.println(" Active ordered broadcasts [" + mQueueName + "]:"); } pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":"); - mOrderedBroadcasts.get(i).dump(pw, " "); + mOrderedBroadcasts.get(i).dump(pw, " ", sdf); } if (dumpPackage == null || (mPendingBroadcast != null && dumpPackage.equals(mPendingBroadcast.callerPackage))) { @@ -1341,7 +1343,7 @@ public final class BroadcastQueue { } pw.println(" Pending broadcast [" + mQueueName + "]:"); if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); + mPendingBroadcast.dump(pw, " ", sdf); } else { pw.println(" (null)"); } @@ -1379,7 +1381,7 @@ public final class BroadcastQueue { if (dumpAll) { pw.print(" Historical Broadcast " + mQueueName + " #"); pw.print(i); pw.println(":"); - r.dump(pw, " "); + r.dump(pw, " ", sdf); } else { pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); pw.print(" "); @@ -1413,7 +1415,6 @@ public final class BroadcastQueue { } // done skipping; dump the remainder of the ring. 'i' is still the ordinal within // the overall broadcast history. - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); do { ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); Intent intent = mBroadcastSummaryHistory[ringIndex]; @@ -1435,9 +1436,19 @@ public final class BroadcastQueue { i++; pw.print(" #"); pw.print(i); pw.print(": "); pw.println(intent.toShortString(false, true, true, false)); - pw.print(" enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); - pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); - pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); + pw.print(" "); + TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex] + - mSummaryHistoryEnqueueTime[ringIndex], pw); + pw.print(" dispatch "); + TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex] + - mSummaryHistoryDispatchTime[ringIndex], pw); + pw.println(" finish"); + pw.print(" enq="); + pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); + pw.print(" disp="); + pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); + pw.print(" fin="); + pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); Bundle bundle = intent.getExtras(); if (bundle != null) { pw.print(" extras: "); pw.println(bundle.toString()); diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 1fbfd9f..b42bcff 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -32,6 +32,7 @@ import android.util.PrintWriterPrinter; import android.util.TimeUtils; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -88,7 +89,7 @@ final class BroadcastRecord extends Binder { ComponentName curComponent; // the receiver class that is currently running. ActivityInfo curReceiver; // info about the receiver that is currently running. - void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) { final long now = SystemClock.uptimeMillis(); pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId); @@ -114,13 +115,19 @@ final class BroadcastRecord extends Binder { pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); } pw.print(prefix); pw.print("enqueueClockTime="); - pw.print(new Date(enqueueClockTime)); + pw.print(sdf.format(new Date(enqueueClockTime))); pw.print(" dispatchClockTime="); - pw.println(new Date(dispatchClockTime)); + pw.println(sdf.format(new Date(dispatchClockTime))); pw.print(prefix); pw.print("dispatchTime="); TimeUtils.formatDuration(dispatchTime, now, pw); + pw.print(" ("); + TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw); + pw.print(" since enq)"); if (finishTime != 0) { pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw); + pw.print(" ("); + TimeUtils.formatDuration(finishTime-dispatchTime, pw); + pw.print(" since disp)"); } else { pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 9c4c978..9cab95b 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -114,6 +114,7 @@ final class ProcessRecord { boolean killed; // True once we know the process has been killed boolean procStateChanged; // Keep track of whether we changed 'setAdj'. boolean reportedInteraction;// Whether we have told usage stats about it being an interaction + long interactionEventTime; // The time we sent the last interaction event long fgInteractionTime; // When we became foreground for interaction purposes String waitingToKill; // Process is waiting to be killed when in the bg, and reason IBinder forcingToForeground;// Token that is forcing this process to be foreground @@ -297,6 +298,10 @@ final class ProcessRecord { if (reportedInteraction || fgInteractionTime != 0) { pw.print(prefix); pw.print("reportedInteraction="); pw.print(reportedInteraction); + if (interactionEventTime != 0) { + pw.print(" time="); + TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw); + } if (fgInteractionTime != 0) { pw.print(" fgInteractionTime="); TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw); diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java index fe5c2da..2d768d8 100644 --- a/services/core/java/com/android/server/content/AppIdleMonitor.java +++ b/services/core/java/com/android/server/content/AppIdleMonitor.java @@ -50,8 +50,8 @@ class AppIdleMonitor extends AppIdleStateChangeListener { } } - boolean isAppIdle(String packageName, int userId) { - return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId); + boolean isAppIdle(String packageName, int uidForAppId, int userId) { + return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, uidForAppId, userId); } @Override diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 334bc18..3ec0bee 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -104,6 +104,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.Set; @@ -2622,9 +2623,18 @@ public class SyncManager { continue; } String packageName = getPackageName(op.target); + ApplicationInfo ai = null; + if (packageName != null) { + try { + ai = mContext.getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS); + } catch (NameNotFoundException e) { + } + } // If app is considered idle, then skip for now and backoff - if (packageName != null - && mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) { + if (ai != null + && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) { increaseBackoffSetting(op); op.appIdle = true; if (isLoggable) { @@ -3370,7 +3380,7 @@ public class SyncManager { if (!smaller.containsKey(key)) { return false; } - if (!bigger.get(key).equals(smaller.get(key))) { + if (!Objects.equals(bigger.get(key), smaller.get(key))) { return false; } } @@ -3378,7 +3388,6 @@ public class SyncManager { } /** - * TODO: Get rid of this when we separate sync settings extras from dev specified extras. * @return true if the provided key is used by the SyncManager in scheduling the sync. */ private static boolean isSyncSetting(String key) { diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index bb4dbc3..835ba17 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -74,6 +74,7 @@ final class ColorFade { // Set to true when the animation context has been fully prepared. private boolean mPrepared; + private boolean mCreatedResources; private int mMode; private final DisplayManagerInternal mDisplayManagerInternal; @@ -169,6 +170,7 @@ final class ColorFade { } // Done. + mCreatedResources = true; mPrepared = true; // Dejanking optimization. @@ -313,18 +315,17 @@ final class ColorFade { } /** - * Dismisses the color fade animation surface and cleans up. + * Dismisses the color fade animation resources. * - * To prevent stray photons from leaking out after the color fade has been - * turned off, it is a good idea to defer dismissing the animation until the - * color fade has been turned back on fully. + * This function destroys the resources that are created for the color fade + * animation but does not clean up the surface. */ - public void dismiss() { + public void dismissResources() { if (DEBUG) { - Slog.d(TAG, "dismiss"); + Slog.d(TAG, "dismissResources"); } - if (mPrepared) { + if (mCreatedResources) { attachEglContext(); try { destroyScreenshotTexture(); @@ -334,8 +335,28 @@ final class ColorFade { } finally { detachEglContext(); } - destroySurface(); + // This is being called with no active context so shouldn't be + // needed but is safer to not change for now. GLES20.glFlush(); + mCreatedResources = false; + } + } + + /** + * Dismisses the color fade animation surface and cleans up. + * + * To prevent stray photons from leaking out after the color fade has been + * turned off, it is a good idea to defer dismissing the animation until the + * color fade has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + if (mPrepared) { + dismissResources(); + destroySurface(); mPrepared = false; } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 45065b8..38c4180 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -859,6 +859,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mPendingScreenOff && target != Display.STATE_OFF) { setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } if (target == Display.STATE_ON) { @@ -932,6 +933,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // A black surface is already hiding the contents of the screen. setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } else if (performScreenOffTransition && mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 2eabd32..9862516 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -187,7 +187,7 @@ final class DisplayPowerState { } /** - * Dismisses the electron beam surface. + * Dismisses the color fade surface. */ public void dismissColorFade() { mColorFade.dismiss(); @@ -195,6 +195,13 @@ final class DisplayPowerState { mColorFadeReady = true; } + /** + * Dismisses the color fade resources. + */ + public void dismissColorFadeResources() { + mColorFade.dismissResources(); + } + /** * Sets the level of the electron beam steering current. * diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 67f6fe1..37326cc 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -838,8 +838,17 @@ public class InputManagerService extends IInputManager.Stub } @Override // Binder call + public int isInTabletMode() { + if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE, + "isInTabletMode()")) { + throw new SecurityException("Requires TABLET_MODE permission"); + } + return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE); + } + + @Override // Binder call public void registerTabletModeChangedListener(ITabletModeChangedListener listener) { - if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER, + if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE, "registerTabletModeChangedListener()")) { throw new SecurityException("Requires TABLET_MODE_LISTENER permission"); } @@ -1586,7 +1595,7 @@ public class InputManagerService extends IInputManager.Stub switchMask); } - if ((switchMask & SW_TABLET_MODE) != 0) { + if ((switchMask & SW_TABLET_MODE_BIT) != 0) { SomeArgs args = SomeArgs.obtain(); args.argi1 = (int) (whenNanos & 0xFFFFFFFF); args.argi2 = (int) (whenNanos >> 32); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index ecda36a..3c50102 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -73,7 +73,7 @@ import com.android.server.job.controllers.TimeController; */ public class JobSchedulerService extends com.android.server.SystemService implements StateChangedListener, JobCompletedListener { - static final boolean DEBUG = false; + public static final boolean DEBUG = false; /** The number of concurrent jobs we run at one time. */ private static final int MAX_JOB_CONTEXTS_COUNT = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; @@ -99,7 +99,7 @@ public class JobSchedulerService extends com.android.server.SystemService * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule * things early. */ - static final int MIN_CONNECTIVITY_COUNT = 2; + static final int MIN_CONNECTIVITY_COUNT = 1; // Run connectivity jobs as soon as ready. /** * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running * some work early. @@ -443,13 +443,16 @@ public class JobSchedulerService extends com.android.server.SystemService } /** - * A job is rescheduled with exponential back-off if the client requests this from their - * execution logic. - * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the - * timeliness of the reschedule. For an idle-mode job, no deadline is given. + * Reschedules the given job based on the job's backoff policy. It doesn't make sense to + * specify an override deadline on a failed job (the failed job will run even though it's not + * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any + * ready job with {@link JobStatus#numFailures} > 0 will be executed. + * * @param failureToReschedule Provided job status that we will reschedule. * @return A newly instantiated JobStatus with the same constraints as the last job except * with adjusted timing constraints. + * + * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH */ private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { final long elapsedNowMillis = SystemClock.elapsedRealtime(); @@ -479,8 +482,9 @@ public class JobSchedulerService extends com.android.server.SystemService } /** - * Called after a periodic has executed so we can to re-add it. We take the last execution time - * of the job to be the time of completion (i.e. the time at which this function is called). + * Called after a periodic has executed so we can reschedule it. We take the last execution + * time of the job to be the time of completion (i.e. the time at which this function is + * called). * This could be inaccurate b/c the job can run for as long as * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead * to underscheduling at least, rather than if we had taken the last execution time to be the @@ -491,7 +495,12 @@ public class JobSchedulerService extends com.android.server.SystemService private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { final long elapsedNow = SystemClock.elapsedRealtime(); // Compute how much of the period is remaining. - long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0); + long runEarly = 0L; + + // If this periodic was rescheduled it won't have a deadline. + if (periodicToReschedule.hasDeadlineConstraint()) { + runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); + } long newEarliestRunTimeElapsed = elapsedNow + runEarly; long period = periodicToReschedule.getJob().getIntervalMillis(); long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index bb4e388..5515393 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -62,13 +62,13 @@ import java.util.concurrent.atomic.AtomicBoolean; * */ public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection { - private static final boolean DEBUG = false; + private static final boolean DEBUG = JobSchedulerService.DEBUG; private static final String TAG = "JobServiceContext"; /** Define the maximum # of jobs allowed to run on a service at once. */ private static final int defaultMaxActiveJobsPerService = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; /** Amount of time a job is allowed to execute for before being considered timed-out. */ - private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; + private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. /** Amount of time the JobScheduler will wait for a response from an app for a message. */ private static final long OP_TIMEOUT_MILLIS = 8 * 1000; @@ -108,7 +108,13 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne int mVerb; private AtomicBoolean mCancelled = new AtomicBoolean(); - /** All the information maintained about the job currently being executed. */ + /** + * All the information maintained about the job currently being executed. + * + * Any reads (dereferences) not done from the handler thread must be synchronized on + * {@link #mLock}. + * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}. + */ private JobStatus mRunningJob; /** Binder to the client service. */ IJobService service; @@ -192,7 +198,8 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne */ JobStatus getRunningJob() { synchronized (mLock) { - return mRunningJob; + return mRunningJob == null ? + null : new JobStatus(mRunningJob); } } @@ -253,15 +260,22 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne */ @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (!name.equals(mRunningJob.getServiceComponent())) { + JobStatus runningJob; + synchronized (mLock) { + // This isn't strictly necessary b/c the JobServiceHandler is running on the main + // looper and at this point we can't get any binder callbacks from the client. Better + // safe than sorry. + runningJob = mRunningJob; + } + if (runningJob == null || !name.equals(runningJob.getServiceComponent())) { mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); return; } this.service = IJobService.Stub.asInterface(service); final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag()); - mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid())); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag()); + mWakeLock.setWorkSource(new WorkSource(runningJob.getUid())); mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); @@ -279,13 +293,15 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * @return True if the binder calling is coming from the client we expect. */ private boolean verifyCallingUid() { - if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { - if (DEBUG) { - Slog.d(TAG, "Stale callback received, ignoring."); + synchronized (mLock) { + if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { + if (DEBUG) { + Slog.d(TAG, "Stale callback received, ignoring."); + } + return false; } - return false; + return true; } - return true; } /** diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 24d4f15..53125c0 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.os.PersistableBundle; import android.os.SystemClock; import android.os.UserHandle; +import android.text.format.DateUtils; import android.util.AtomicFile; import android.util.ArraySet; import android.util.Pair; @@ -552,9 +553,10 @@ public class JobStore { return null; } - Pair<Long, Long> runtimes; + // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load. + Pair<Long, Long> elapsedRuntimes; try { - runtimes = buildExecutionTimesFromXml(parser); + elapsedRuntimes = buildExecutionTimesFromXml(parser); } catch (NumberFormatException e) { if (DEBUG) { Slog.d(TAG, "Error parsing execution time parameters, skipping."); @@ -562,22 +564,45 @@ public class JobStore { return null; } + final long elapsedNow = SystemClock.elapsedRealtime(); if (XML_TAG_PERIODIC.equals(parser.getName())) { try { String val = parser.getAttributeValue(null, "period"); - jobBuilder.setPeriodic(Long.valueOf(val)); + final long periodMillis = Long.valueOf(val); + jobBuilder.setPeriodic(periodMillis); + // As a sanity check, cap the recreated run time to be no later than 2 periods + // from now. This is the latest the periodic could be pushed out. This could + // happen if the periodic ran early (at the start of its period), and then the + // device rebooted. + if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) { + final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis; + final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis; + Slog.w(TAG, + String.format("Periodic job for uid='%d' persisted run-time is" + + " too big [%s, %s]. Clamping to [%s,%s]", + uid, + DateUtils.formatElapsedTime(elapsedRuntimes.first / 1000), + DateUtils.formatElapsedTime(elapsedRuntimes.second / 1000), + DateUtils.formatElapsedTime( + clampedEarlyRuntimeElapsed / 1000), + DateUtils.formatElapsedTime( + clampedLateRuntimeElapsed / 1000)) + ); + elapsedRuntimes = + Pair.create(clampedEarlyRuntimeElapsed, clampedLateRuntimeElapsed); + } } catch (NumberFormatException e) { Slog.d(TAG, "Error reading periodic execution criteria, skipping."); return null; } } else if (XML_TAG_ONEOFF.equals(parser.getName())) { try { - if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) { - jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); + if (elapsedRuntimes.first != JobStatus.NO_EARLIEST_RUNTIME) { + jobBuilder.setMinimumLatency(elapsedRuntimes.first - elapsedNow); } - if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) { + if (elapsedRuntimes.second != JobStatus.NO_LATEST_RUNTIME) { jobBuilder.setOverrideDeadline( - runtimes.second - SystemClock.elapsedRealtime()); + elapsedRuntimes.second - elapsedNow); } } catch (NumberFormatException e) { Slog.d(TAG, "Error reading job execution criteria, skipping."); @@ -598,7 +623,8 @@ public class JobStore { do { eventType = parser.next(); } while (eventType == XmlPullParser.TEXT); - if (!(eventType == XmlPullParser.START_TAG && XML_TAG_EXTRAS.equals(parser.getName()))) { + if (!(eventType == XmlPullParser.START_TAG + && XML_TAG_EXTRAS.equals(parser.getName()))) { if (DEBUG) { Slog.d(TAG, "Error reading extras, skipping."); } @@ -609,7 +635,8 @@ public class JobStore { jobBuilder.setExtras(extras); parser.nextTag(); // Consume </extras> - return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second); + return new JobStatus( + jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second); } private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 02d4f40..6fc02f6 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -67,7 +67,7 @@ public class AppIdleController extends StateController { mTrackedTasks.add(jobStatus); String packageName = jobStatus.job.getService().getPackageName(); final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, - jobStatus.getUserId()); + jobStatus.uId, jobStatus.getUserId()); if (DEBUG) { Slog.d(LOG_TAG, "Start tracking, setting idle state of " + packageName + " to " + appIdle); @@ -108,7 +108,7 @@ public class AppIdleController extends StateController { for (JobStatus task : mTrackedTasks) { String packageName = task.job.getService().getPackageName(); final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, - task.getUserId()); + task.uId, task.getUserId()); if (DEBUG) { Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle); } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 69c63f3..c02611f 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -82,6 +82,13 @@ public class JobStatus { this.numFailures = numFailures; } + /** Copy constructor. */ + public JobStatus(JobStatus jobStatus) { + this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures()); + this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime(); + this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed(); + } + /** Create a newly scheduled job. */ public JobStatus(JobInfo job, int uId) { this(job, uId, 0); diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index cda7c32..21c30c7 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -18,6 +18,7 @@ package com.android.server.job.controllers; import android.content.Context; +import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import java.io.PrintWriter; @@ -28,7 +29,7 @@ import java.io.PrintWriter; * are ready to run, or whether they must be stopped. */ public abstract class StateController { - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = JobSchedulerService.DEBUG; protected Context mContext; protected StateChangedListener mStateChangedListener; protected boolean mDeviceIdleMode; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index f041f0d..bf7560e 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -39,17 +39,17 @@ import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; +import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.RULE_UNKNOWN; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; -import static android.net.NetworkPolicyManager.dumpPolicy; -import static android.net.NetworkPolicyManager.dumpRules; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; @@ -108,6 +108,7 @@ import android.net.LinkProperties; import android.net.NetworkIdentity; import android.net.NetworkInfo; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkState; import android.net.NetworkTemplate; @@ -138,6 +139,7 @@ import android.text.format.Time; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.DebugUtils; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Pair; @@ -147,8 +149,6 @@ import android.util.SparseIntArray; import android.util.TrustedTime; import android.util.Xml; -import com.android.server.DeviceIdleController; -import com.android.server.EventLogTags; import libcore.io.IoUtils; import com.android.internal.R; @@ -156,6 +156,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.DeviceIdleController; +import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.google.android.collect.Lists; @@ -279,6 +281,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidPolicy = new SparseIntArray(); /** Currently derived rules for each UID. */ final SparseIntArray mUidRules = new SparseIntArray(); + + final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); + final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -446,14 +452,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // read policy from disk readPolicyLocked(); - if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) { - updateRulesForGlobalChangeLocked(false); - updateNotificationsLocked(); - } else { - // If we are not in any special mode, we just need to make sure the current - // app idle state is updated. - updateRulesForAppIdleLocked(); - } + updateRulesForGlobalChangeLocked(false); + updateNotificationsLocked(); } updateScreenOn(); @@ -1801,7 +1801,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mDeviceIdleMode != enabled) { mDeviceIdleMode = enabled; if (mSystemReady) { - updateRulesForDeviceIdleLocked(); + // Device idle change means we need to rebuild rules for all + // known apps, so do a global refresh. + updateRulesForGlobalChangeLocked(false); } if (enabled) { EventLogTags.writeDeviceIdleOnPhase("net"); @@ -1939,7 +1941,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); fout.print(" policy="); - dumpPolicy(fout, policy); + fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy)); fout.println(); } fout.decreaseIndent(); @@ -1984,18 +1986,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("UID="); fout.print(uid); - int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); fout.print(" state="); fout.print(state); fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)"); - fout.print(" rules="); - final int rulesIndex = mUidRules.indexOfKey(uid); - if (rulesIndex < 0) { - fout.print("UNKNOWN"); - } else { - dumpRules(fout, mUidRules.valueAt(rulesIndex)); - } + final int rule = mUidRules.get(uid, RULE_UNKNOWN); + fout.print(" rule="); + fout.print(DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule)); fout.println(); } @@ -2030,7 +2028,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForUidStateChangeLocked(uid, oldUidState, uidState); if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState) != isProcStateAllowedWhileIdle(uidState)) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2044,7 +2042,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForUidStateChangeLocked(uid, oldUidState, ActivityManager.PROCESS_STATE_CACHED_EMPTY); if (mDeviceIdleMode) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2091,7 +2089,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mDeviceIdleMode) { // sync the whitelists before enable dozable chain. We don't care about the rules if // we are disabling the chain. - SparseIntArray uidRules = new SparseIntArray(); + final SparseIntArray uidRules = mUidFirewallDozableRules; + uidRules.clear(); final List<UserInfo> users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2115,6 +2114,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules); } + enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); } @@ -2128,11 +2128,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); } } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleLocked() { + final SparseIntArray uidRules = mUidFirewallStandbyRules; + uidRules.clear(); + // Fully update the app idle firewall chain. - SparseIntArray uidRules = new SparseIntArray(); final List<UserInfo> users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2143,6 +2147,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } + setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules); } @@ -2155,11 +2160,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleParoleLocked() { boolean enableChain = !mUsageStats.isAppIdleParoleOn(); enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain); + updateRulesForUidsLocked(mUidFirewallStandbyRules); } /** @@ -2222,13 +2230,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int userId = UserHandle.getUserId(uid); for (String packageName : packages) { - if (!mUsageStats.isAppIdle(packageName, userId)) { + if (!mUsageStats.isAppIdle(packageName, uid, userId)) { return false; } } return true; } + void updateRulesForUidsLocked(SparseIntArray uids) { + for (int i = 0; i < uids.size(); i++) { + updateRulesForUidLocked(uids.keyAt(i)); + } + } + /** * Applies network rules to bandwidth and firewall controllers based on uid policy. * @param uid The uid for which to apply the latest policy @@ -2250,8 +2264,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); final boolean uidForeground = isUidForegroundLocked(uid); - // derive active rules based on policy and active state - + // Derive active rules based on policy and active state int appId = UserHandle.getAppId(uid); int uidRules = RULE_ALLOW_ALL; if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) { @@ -2274,20 +2287,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - final int oldRules = mUidRules.get(uid); + // Check dozable state, which is whitelist + if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE) + && mUidFirewallDozableRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) { + uidRules = RULE_REJECT_ALL; + } + + // Check standby state, which is blacklist + if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY) + && mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) { + uidRules = RULE_REJECT_ALL; + } + final int oldRules = mUidRules.get(uid); if (uidRules == RULE_ALLOW_ALL) { mUidRules.delete(uid); } else { mUidRules.put(uid, uidRules); } - // Update bandwidth rules if necessary - final boolean oldRejectMetered = (oldRules & RULE_REJECT_METERED) != 0; - final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - if (oldRejectMetered != rejectMetered) { - setUidNetworkRules(uid, rejectMetered); - } + final boolean rejectMetered = (uidRules == RULE_REJECT_METERED); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners if (oldRules != uidRules) { @@ -2474,6 +2494,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void setUidFirewallRule(int chain, int uid, int rule) { + if (chain == FIREWALL_CHAIN_DOZABLE) { + mUidFirewallDozableRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_STANDBY) { + mUidFirewallStandbyRules.put(uid, rule); + } + try { mNetworkManager.setFirewallUidRule(chain, uid, rule); } catch (IllegalStateException e) { @@ -2487,9 +2513,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void enableFirewallChainLocked(int chain, boolean enable) { - if (mFirewallChainStates.indexOfKey(chain) >= 0 && - mFirewallChainStates.get(chain) == enable) { - // All is the same, nothing to do. + if (mFirewallChainStates.get(chain, false) == enable) { + // All is the same, nothing to do. This relies on the fact that netd has child + // chains default detached. return; } mFirewallChainStates.put(chain, enable); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 91ae31d..358b773 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1798,13 +1798,16 @@ public class PackageManagerService extends IPackageManager.Stub { return; } - PermissionsState permissionsState = sb.getPermissionsState(); - - for (String permission : pkg.requestedPermissions) { - BasePermission bp = mSettings.mPermissions.get(permission); - if (bp != null && bp.isRuntime() && (grantedPermissions == null - || ArrayUtils.contains(grantedPermissions, permission))) { - permissionsState.grantRuntimePermission(bp, userId); + synchronized (mPackages) { + for (String permission : pkg.requestedPermissions) { + BasePermission bp = mSettings.mPermissions.get(permission); + if (bp != null && (bp.isRuntime() || bp.isDevelopment()) + && (grantedPermissions == null + || ArrayUtils.contains(grantedPermissions, permission)) + && (getPermissionFlags(permission, pkg.packageName, userId) + & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) { + grantRuntimePermission(pkg.packageName, permission, userId); + } } } } @@ -2389,7 +2392,7 @@ public class PackageManagerService extends IPackageManager.Stub { + mSdkVersion + "; regranting permissions for internal storage"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); ver.sdkVersion = mSdkVersion; // Remove any stale app permissions (declared permission that now are undeclared @@ -2878,26 +2881,38 @@ public class PackageManagerService extends IPackageManager.Stub { @Override public int getPackageUid(String packageName, int userId) { + return getPackageUidEtc(packageName, 0, userId); + } + + @Override + public int getPackageUidEtc(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid"); // reader synchronized (mPackages) { - PackageParser.Package p = mPackages.get(packageName); - if(p != null) { + final PackageParser.Package p = mPackages.get(packageName); + if (p != null) { return UserHandle.getUid(userId, p.applicationInfo.uid); } - PackageSetting ps = mSettings.mPackages.get(packageName); - if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { - return -1; + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + return UserHandle.getUid(userId, ps.appId); + } } - p = ps.pkg; - return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1; } + + return -1; } @Override - public int[] getPackageGids(String packageName, int userId) throws RemoteException { + public int[] getPackageGids(String packageName, int userId) { + return getPackageGidsEtc(packageName, 0, userId); + } + + @Override + public int[] getPackageGidsEtc(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) { return null; } @@ -2907,14 +2922,17 @@ public class PackageManagerService extends IPackageManager.Stub { // reader synchronized (mPackages) { - PackageParser.Package p = mPackages.get(packageName); - if (DEBUG_PACKAGE_INFO) { - Log.v(TAG, "getPackageGids" + packageName + ": " + p); - } + final PackageParser.Package p = mPackages.get(packageName); if (p != null) { PackageSetting ps = (PackageSetting) p.mExtras; return ps.getPermissionsState().computeGids(userId); } + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + return ps.getPermissionsState().computeGids(userId); + } + } } return null; @@ -3676,7 +3694,8 @@ public class PackageManagerService extends IPackageManager.Stub { killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED); } }); - } break; + } + break; } mOnPermissionChangeListeners.onPermissionsChanged(uid); @@ -4947,18 +4966,13 @@ public class PackageManagerService extends IPackageManager.Stub { // First try to add the "always" resolution(s) for the current user, if any if (alwaysList.size() > 0) { result.addAll(alwaysList); - // if there is an "always" for the parent user, add it. - } else if (xpDomainInfo != null && xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { - result.add(xpDomainInfo.resolveInfo); } else { // Add all undefined apps as we want them to appear in the disambiguation dialog. result.addAll(undefinedList); + // Maybe add one for the other profile. if (xpDomainInfo != null && ( xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED - || xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK)) { + != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) { result.add(xpDomainInfo.resolveInfo); } includeBrowser = true; @@ -6327,41 +6341,6 @@ public class PackageManagerService extends IPackageManager.Stub { it.remove(); } } - // Give priority to system apps. - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding system app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } - // Give priority to updated system apps. - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (pkg.isUpdatedSystemApp()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding updated system app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } - // Give priority to apps that listen for boot complete. - intent = new Intent(Intent.ACTION_BOOT_COMPLETED); - pkgNames = getPackageNamesForIntent(intent); - for (Iterator<PackageParser.Package> it = pkgs.iterator(); it.hasNext();) { - PackageParser.Package pkg = it.next(); - if (pkgNames.contains(pkg.packageName)) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Adding boot app " + sortedPkgs.size() + ": " + pkg.packageName); - } - sortedPkgs.add(pkg); - it.remove(); - } - } // Filter out packages that aren't recently used. filterRecentlyUsedApps(pkgs); // Add all remaining apps. @@ -7926,8 +7905,8 @@ public class PackageManagerService extends IPackageManager.Stub { // We would never need to extract libs for forward-locked and external packages, // since the container service will do it for us. We shouldn't attempt to // extract libs from system app when it was not updated. - if (pkg.isForwardLocked() || isExternal(pkg) || - (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) { + if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() || + (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) { extractLibs = false; } @@ -8502,7 +8481,7 @@ public class PackageManagerService extends IPackageManager.Stub { final String codePath = pkg.codePath; final File codeFile = new File(codePath); final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); - final boolean asecApp = info.isForwardLocked() || isExternal(info); + final boolean asecApp = info.isForwardLocked() || info.isExternalAsec(); info.nativeLibraryRootDir = null; info.nativeLibraryRootRequiresIsa = false; @@ -8907,8 +8886,14 @@ public class PackageManagerService extends IPackageManager.Stub { static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1; static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2; + private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, + int flags) { + final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null; + updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags); + } + private void updatePermissionsLPw(String changingPkg, - PackageParser.Package pkgInfo, int flags) { + PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) { // Make sure there are no dangling permission trees. Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { @@ -8977,14 +8962,21 @@ public class PackageManagerService extends IPackageManager.Stub { if ((flags&UPDATE_PERMISSIONS_ALL) != 0) { for (PackageParser.Package pkg : mPackages.values()) { if (pkg != pkgInfo) { - grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0, - changingPkg); + // Only replace for packages on requested volume + final String volumeUuid = getVolumeUuidForPackage(pkg); + final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0) + && Objects.equals(replaceVolumeUuid, volumeUuid); + grantPermissionsLPw(pkg, replace, changingPkg); } } } if (pkgInfo != null) { - grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg); + // Only replace for packages on requested volume + final String volumeUuid = getVolumeUuidForPackage(pkgInfo); + final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0) + && Objects.equals(replaceVolumeUuid, volumeUuid); + grantPermissionsLPw(pkgInfo, replace, changingPkg); } } @@ -9011,6 +9003,7 @@ public class PackageManagerService extends IPackageManager.Stub { final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); + boolean runtimePermissionsRevoked = false; int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY; boolean changedInstallPermission = false; @@ -9020,6 +9013,17 @@ public class PackageManagerService extends IPackageManager.Stub { if (!ps.isSharedUser()) { origPermissions = new PermissionsState(permissionsState); permissionsState.reset(); + } else { + // We need to know only about runtime permission changes since the + // calling code always writes the install permissions state but + // the runtime ones are written only if changed. The only cases of + // changed runtime permissions here are promotion of an install to + // runtime and revocation of a runtime from a shared user. + changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw( + ps.sharedUser, UserManagerService.getInstance().getUserIds()); + if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) { + runtimePermissionsRevoked = true; + } } } @@ -9235,9 +9239,11 @@ public class PackageManagerService extends IPackageManager.Stub { ps.installPermissionsFixed = true; } - // Persist the runtime permissions state for users with changes. + // Persist the runtime permissions state for users with changes. If permissions + // were revoked because no app in the shared user declares them we have to + // write synchronously to avoid losing runtime permissions state. for (int userId : changedRuntimePermissionUserIds) { - mSettings.writeRuntimePermissionsForUserLPr(userId, false); + mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked); } } @@ -12762,6 +12768,66 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) { + // Collect all used permissions in the UID + ArraySet<String> usedPermissions = new ArraySet<>(); + final int packageCount = su.packages.size(); + for (int i = 0; i < packageCount; i++) { + PackageSetting ps = su.packages.valueAt(i); + if (ps.pkg == null) { + continue; + } + final int requestedPermCount = ps.pkg.requestedPermissions.size(); + for (int j = 0; j < requestedPermCount; j++) { + String permission = ps.pkg.requestedPermissions.get(j); + BasePermission bp = mSettings.mPermissions.get(permission); + if (bp != null) { + usedPermissions.add(permission); + } + } + } + + PermissionsState permissionsState = su.getPermissionsState(); + // Prune install permissions + List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); + final int installPermCount = installPermStates.size(); + for (int i = installPermCount - 1; i >= 0; i--) { + PermissionState permissionState = installPermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + } + } + } + + int[] runtimePermissionChangedUserIds = EmptyArray.INT; + + // Prune runtime permissions + for (int userId : allUserIds) { + List<PermissionState> runtimePermStates = permissionsState + .getRuntimePermissionStates(userId); + final int runtimePermCount = runtimePermStates.size(); + for (int i = runtimePermCount - 1; i >= 0; i--) { + PermissionState permissionState = runtimePermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + runtimePermissionChangedUserIds = ArrayUtils.appendInt( + runtimePermissionChangedUserIds, userId); + } + } + } + } + + return runtimePermissionChangedUserIds; + } + private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) { @@ -13259,6 +13325,18 @@ public class PackageManagerService extends IPackageManager.Stub { return installFlags; } + private String getVolumeUuidForPackage(PackageParser.Package pkg) { + if (isExternal(pkg)) { + if (TextUtils.isEmpty(pkg.volumeUuid)) { + return StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + return pkg.volumeUuid; + } + } else { + return StorageManager.UUID_PRIVATE_INTERNAL; + } + } + private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) { if (isExternal(pkg)) { if (TextUtils.isEmpty(pkg.volumeUuid)) { @@ -14301,7 +14379,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (ps != null) { libDirRoot = ps.legacyNativeLibraryPathString; } - if (p != null && (isExternal(p) || p.isForwardLocked())) { + if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) { final long token = Binder.clearCallingIdentity(); try { String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath()); @@ -16162,7 +16240,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (isMounted) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr); + loadMediaPackages(processCids, uidArr, externalStorage); startCleaningPackages(); mInstallerService.onSecureContainersAvailable(); } else { @@ -16217,7 +16295,8 @@ public class PackageManagerService extends IPackageManager.Stub { * the cid is added to list of removeCids. We currently don't delete stale * containers. */ - private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr) { + private void loadMediaPackages(ArrayMap<AsecInstallArgs, String> processCids, int[] uidArr, + boolean externalStorage) { ArrayList<String> pkgList = new ArrayList<String>(); Set<AsecInstallArgs> keys = processCids.keySet(); @@ -16289,7 +16368,10 @@ public class PackageManagerService extends IPackageManager.Stub { // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. - final VersionInfo ver = mSettings.getExternalVersion(); + final VersionInfo ver = externalStorage ? mSettings.getExternalVersion() + : mSettings.getInternalVersion(); + final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL + : StorageManager.UUID_PRIVATE_INTERNAL; int updateFlags = UPDATE_PERMISSIONS_ALL; if (ver.sdkVersion != mSdkVersion) { @@ -16297,7 +16379,7 @@ public class PackageManagerService extends IPackageManager.Stub { + mSdkVersion + "; regranting permissions for external"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, volumeUuid, updateFlags); // Yay, everything is now upgraded ver.forceCurrent(); @@ -16431,7 +16513,7 @@ public class PackageManagerService extends IPackageManager.Stub { + mSdkVersion + "; regranting permissions for " + vol.fsUuid); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, vol.fsUuid, updateFlags); // Yay, everything is now upgraded ver.forceCurrent(); @@ -16605,13 +16687,18 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); final int moveId = mNextMoveId.getAndIncrement(); - try { - movePackageInternal(packageName, volumeUuid, moveId); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to move " + packageName, e); - mMoveCallbacks.notifyStatusChanged(moveId, - PackageManager.MOVE_FAILED_INTERNAL_ERROR); - } + mHandler.post(new Runnable() { + @Override + public void run() { + try { + movePackageInternal(packageName, volumeUuid, moveId); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to move " + packageName, e); + mMoveCallbacks.notifyStatusChanged(moveId, + PackageManager.MOVE_FAILED_INTERNAL_ERROR); + } + } + }); return moveId; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index e02e4da..635f46e 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -240,6 +240,7 @@ abstract class PackageSettingBase extends SettingBase { keySetData = base.keySetData; verificationInfo = base.verificationInfo; installerPackageName = base.installerPackageName; + volumeUuid = base.volumeUuid; } private PackageUserState modifyUserState(int userId) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index da82853..7b95106 100755 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -545,7 +545,18 @@ final class Settings { ArrayList<String> removeStage = new ArrayList<String>(); for (Map.Entry<String,SharedUserSetting> entry : mSharedUsers.entrySet()) { final SharedUserSetting sus = entry.getValue(); - if (sus == null || sus.packages.size() == 0) { + if (sus == null) { + removeStage.add(entry.getKey()); + continue; + } + // remove packages that are no longer installed + for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) { + PackageSetting ps = iter.next(); + if (mPackages.get(ps.name) == null) { + iter.remove(); + } + } + if (sus.packages.size() == 0) { removeStage.add(entry.getKey()); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c8d329c..dd4d3ab 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3567,7 +3567,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Display task switcher for ALT-TAB. if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { - if (mRecentAppsHeldModifiers == 0 && !keyguardOn) { + if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { mRecentAppsHeldModifiers = shiftlessModifiers; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 0f45e39..342a2ac 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1112,6 +1112,8 @@ class WindowStateAnimator { mAnimator.getScreenRotationAnimationLocked(displayId); final boolean screenAnimation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); + + mHasClipRect = false; if (selfTransformation || attachedTransformation != null || appTransformation != null || screenAnimation) { // cache often used attributes locally @@ -1187,7 +1189,6 @@ class WindowStateAnimator { // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; - mHasClipRect = false; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java index 334f708..df44b11 100644 --- a/services/net/java/android/net/dhcp/DhcpAckPacket.java +++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java @@ -46,7 +46,7 @@ class DhcpAckPacket extends DhcpPacket { return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + - ", gateway " + mGateway + dnsServers + + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime; } @@ -79,7 +79,7 @@ class DhcpAckPacket extends DhcpPacket { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index e0d2ac1..c9efc69 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -299,6 +299,7 @@ public class DhcpClient extends BaseDhcpStateMachine { Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1); Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName); Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1); + Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0); Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT); NetworkUtils.protectFromVpn(mUdpSock); } catch(SocketException|ErrnoException e) { @@ -308,6 +309,16 @@ public class DhcpClient extends BaseDhcpStateMachine { return true; } + private boolean connectUdpSock(Inet4Address to) { + try { + Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER); + return true; + } catch (SocketException|ErrnoException e) { + Log.e(TAG, "Error connecting UDP socket", e); + return false; + } + } + private static void closeQuietly(FileDescriptor fd) { try { IoBridge.closeAndSignalBlockedThreads(fd); @@ -325,7 +336,7 @@ public class DhcpClient extends BaseDhcpStateMachine { try { mNMService.setInterfaceConfig(mIfaceName, ifcg); } catch (RemoteException|IllegalStateException e) { - Log.e(TAG, "Error configuring IP address : " + e); + Log.e(TAG, "Error configuring IP address " + address + ": ", e); return false; } return true; @@ -345,21 +356,22 @@ public class DhcpClient extends BaseDhcpStateMachine { public void run() { maybeLog("Receive thread started"); while (!stopped) { + int length = 0; // Or compiler can't tell it's initialized if a parse error occurs. try { - int length = Os.read(mPacketSock, mPacket, 0, mPacket.length); + length = Os.read(mPacketSock, mPacket, 0, mPacket.length); DhcpPacket packet = null; packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2); - if (packet != null) { - maybeLog("Received packet: " + packet); - sendMessage(CMD_RECEIVED_PACKET, packet); - } else if (PACKET_DBG) { - Log.d(TAG, - "Can't parse packet" + HexDump.dumpHexString(mPacket, 0, length)); - } + maybeLog("Received packet: " + packet); + sendMessage(CMD_RECEIVED_PACKET, packet); } catch (IOException|ErrnoException e) { if (!stopped) { Log.e(TAG, "Read error", e); } + } catch (DhcpPacket.ParseException e) { + Log.e(TAG, "Can't parse packet: " + e.getMessage()); + if (PACKET_DBG) { + Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length)); + } } } maybeLog("Receive thread stopped"); @@ -376,8 +388,10 @@ public class DhcpClient extends BaseDhcpStateMachine { maybeLog("Broadcasting " + description); Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); } else { - maybeLog("Unicasting " + description + " to " + to.getHostAddress()); - Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); + // It's safe to call getpeername here, because we only send unicast packets if we + // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter. + maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock)); + Os.write(mUdpSock, buf); } } catch(ErrnoException|IOException e) { Log.e(TAG, "Can't send packet: ", e); @@ -403,9 +417,10 @@ public class DhcpClient extends BaseDhcpStateMachine { encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); + String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + - " to=" + serverAddress.getHostAddress(); + " serverid=" + serverStr; return transmitPacket(packet, description, to); } @@ -789,6 +804,7 @@ public class DhcpClient extends BaseDhcpStateMachine { transitionTo(mDhcpBoundState); } } else if (packet instanceof DhcpNakPacket) { + // TODO: Wait a while before returning into INIT state. Log.d(TAG, "Received NAK, returning to INIT"); mOffer = null; transitionTo(mDhcpInitState); @@ -806,10 +822,9 @@ public class DhcpClient extends BaseDhcpStateMachine { @Override public void enter() { super.enter(); - if (setIpAddress(mDhcpLease.ipAddress)) { - maybeLog("Configured IP address " + mDhcpLease.ipAddress); - } else { - Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress); + if (!setIpAddress(mDhcpLease.ipAddress) || + (mDhcpLease.serverAddress != null && + !connectUdpSock((mDhcpLease.serverAddress)))) { notifyFailure(); // There's likely no point in going into DhcpInitState here, we'll probably just // repeat the transaction, get the same IP address as before, and fail. @@ -865,11 +880,15 @@ public class DhcpClient extends BaseDhcpStateMachine { } protected boolean sendPacket() { + // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but... + // http://b/25343517 . Try to make things work anyway by using broadcast renews. + Inet4Address to = (mDhcpLease.serverAddress != null) ? + mDhcpLease.serverAddress : INADDR_BROADCAST; return sendRequestPacket( (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr INADDR_ANY, // DHCP_REQUESTED_IP - INADDR_ANY, // DHCP_SERVER_IDENTIFIER - (Inet4Address) mDhcpLease.serverAddress); // packet destination address + null, // DHCP_SERVER_IDENTIFIER + to); // packet destination address } protected void receivePacket(DhcpPacket packet) { diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java index 7ca7100..99154ef 100644 --- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java +++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java @@ -48,7 +48,7 @@ class DhcpOfferPacket extends DhcpPacket { } return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + - dnsServers + ", gateway " + mGateway + + dnsServers + ", gateways " + mGateways + " lease time " + mLeaseTime + ", domain " + mDomainName; } @@ -81,7 +81,7 @@ class DhcpOfferPacket extends DhcpPacket { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index cbf8fc2..8927bfa 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -114,6 +114,11 @@ abstract class DhcpPacket { protected static final int MAX_LENGTH = 1500; /** + * The magic cookie that identifies this as a DHCP packet instead of BOOTP. + */ + private static final int DHCP_MAGIC_COOKIE = 0x63825363; + + /** * DHCP Optional Type: DHCP Subnet Mask */ protected static final byte DHCP_SUBNET_MASK = 1; @@ -123,7 +128,7 @@ abstract class DhcpPacket { * DHCP Optional Type: DHCP Router */ protected static final byte DHCP_ROUTER = 3; - protected Inet4Address mGateway; + protected List <Inet4Address> mGateways; /** * DHCP Optional Type: DHCP DNS Server @@ -403,7 +408,7 @@ abstract class DhcpPacket { (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes + 64 // empty server host name (64 bytes) + 128); // empty boot file name (128 bytes) - buf.putInt(0x63825363); // magic number + buf.putInt(DHCP_MAGIC_COOKIE); // magic number finishPacket(buf); // round up to an even number of octets @@ -668,6 +673,20 @@ abstract class DhcpPacket { return new String(bytes, 0, length, StandardCharsets.US_ASCII); } + private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT); + } + + private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER); + } + + public static class ParseException extends Exception { + public ParseException(String msg, Object... args) { + super(String.format(msg, args)); + } + } + /** * Creates a concrete DhcpPacket from the supplied ByteBuffer. The * buffer may have an L2 encapsulation (which is the full EthernetII @@ -677,7 +696,7 @@ abstract class DhcpPacket { * A subset of the optional parameters are parsed and are stored * in object fields. */ - public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) + public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException { // bootp parameters int transactionId; @@ -687,8 +706,8 @@ abstract class DhcpPacket { Inet4Address nextIp; Inet4Address relayIp; byte[] clientMac; - List<Inet4Address> dnsServers = new ArrayList<Inet4Address>(); - Inet4Address gateway = null; // aka router + List<Inet4Address> dnsServers = new ArrayList<>(); + List<Inet4Address> gateways = new ArrayList<>(); // aka router Inet4Address serverIdentifier = null; Inet4Address netMask = null; String message = null; @@ -720,7 +739,8 @@ abstract class DhcpPacket { // check to see if we need to parse L2, IP, and UDP encaps if (pktType == ENCAP_L2) { if (packet.remaining() < MIN_PACKET_LENGTH_L2) { - return null; + throw new ParseException("L2 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L2); } byte[] l2dst = new byte[6]; @@ -732,18 +752,20 @@ abstract class DhcpPacket { short l2type = packet.getShort(); if (l2type != OsConstants.ETH_P_IP) - return null; + throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x", + l2type, OsConstants.ETH_P_IP); } if (pktType <= ENCAP_L3) { if (packet.remaining() < MIN_PACKET_LENGTH_L3) { - return null; + throw new ParseException("L3 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L3); } byte ipTypeAndLength = packet.get(); int ipVersion = (ipTypeAndLength & 0xf0) >> 4; if (ipVersion != 4) { - return null; + throw new ParseException("Invalid IP version %d", ipVersion); } // System.out.println("ipType is " + ipType); @@ -759,8 +781,9 @@ abstract class DhcpPacket { ipSrc = readIpAddress(packet); ipDst = readIpAddress(packet); - if (ipProto != IP_TYPE_UDP) // UDP - return null; + if (ipProto != IP_TYPE_UDP) { + throw new ParseException("Protocol not UDP: %d", ipProto); + } // Skip options. This cannot cause us to read beyond the end of the buffer because the // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than @@ -776,13 +799,19 @@ abstract class DhcpPacket { short udpLen = packet.getShort(); short udpChkSum = packet.getShort(); - if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT)) + // Only accept packets to or from the well-known client port (expressly permitting + // packets from ports other than the well-known server port; http://b/24687559), and + // server-to-server packets, e.g. for relays. + if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) && + !isPacketServerToServer(udpSrcPort, udpDstPort)) { return null; + } } // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length. if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) { - return null; + throw new ParseException("Invalid type or BOOTP packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_BOOTP); } byte type = packet.get(); @@ -805,7 +834,7 @@ abstract class DhcpPacket { packet.get(ipv4addr); relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); } catch (UnknownHostException ex) { - return null; + throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr)); } // Some DHCP servers have been known to announce invalid client hardware address values such @@ -828,8 +857,10 @@ abstract class DhcpPacket { int dhcpMagicCookie = packet.getInt(); - if (dhcpMagicCookie != 0x63825363) - return null; + if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) { + throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie, + DHCP_MAGIC_COOKIE); + } // parse options boolean notFinishedOptions = true; @@ -852,8 +883,9 @@ abstract class DhcpPacket { expectedLen = 4; break; case DHCP_ROUTER: - gateway = readIpAddress(packet); - expectedLen = 4; + for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { + gateways.add(readIpAddress(packet)); + } break; case DHCP_DNS_SERVER: for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { @@ -937,18 +969,20 @@ abstract class DhcpPacket { } if (expectedLen != optionLen) { - return null; + throw new ParseException("Invalid length %d for option %d, expected %d", + optionLen, optionType, expectedLen); } } } catch (BufferUnderflowException e) { - return null; + throw new ParseException("BufferUnderflowException"); } } DhcpPacket newPacket; switch(dhcpType) { - case -1: return null; + case (byte) 0xFF: + throw new ParseException("No DHCP message type option"); case DHCP_MESSAGE_TYPE_DISCOVER: newPacket = new DhcpDiscoverPacket( transactionId, secs, clientMac, broadcast); @@ -981,14 +1015,13 @@ abstract class DhcpPacket { clientMac); break; default: - System.out.println("Unimplemented type: " + dhcpType); - return null; + throw new ParseException("Unimplemented DHCP type %d", dhcpType); } newPacket.mBroadcastAddress = bcAddr; newPacket.mDnsServers = dnsServers; newPacket.mDomainName = domainName; - newPacket.mGateway = gateway; + newPacket.mGateways = gateways; newPacket.mHostName = hostName; newPacket.mLeaseTime = leaseTime; newPacket.mMessage = message; @@ -1009,7 +1042,7 @@ abstract class DhcpPacket { * Parse a packet from an array of bytes, stopping at the given length. */ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) - { + throws ParseException { ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); return decodeFullPacket(buffer, pktType); } @@ -1044,7 +1077,11 @@ abstract class DhcpPacket { } catch (IllegalArgumentException e) { return null; } - results.gateway = mGateway; + + if (mGateways.size() > 0) { + results.gateway = mGateways.get(0); + } + results.dnsServers.addAll(mDnsServers); results.domains = mDomainName; results.serverAddress = mServerIdentifier; @@ -1086,11 +1123,11 @@ abstract class DhcpPacket { public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List<Inet4Address> dnsServers, + List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; @@ -1106,11 +1143,11 @@ abstract class DhcpPacket { public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List<Inet4Address> dnsServers, + List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java index cd3b8bb..7e60bf1 100644 --- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java +++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java @@ -117,7 +117,7 @@ public class DhcpPacketTest extends TestCase { private void assertDomainAndVendorInfoParses( String expectedDomain, byte[] domainBytes, - String expectedVendorInfo, byte[] vendorInfoBytes) { + String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER) .setDomainBytes(domainBytes) .setVendorInfoBytes(vendorInfoBytes) @@ -158,17 +158,25 @@ public class DhcpPacketTest extends TestCase { } private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime, - long leaseTimeMillis, byte[] leaseTimeBytes) { + long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception { TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER); if (leaseTimeBytes != null) { testPacket.setLeaseTimeBytes(leaseTimeBytes); } ByteBuffer packet = testPacket.build(); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + DhcpPacket offerPacket = null; + if (!expectValid) { - assertNull(offerPacket); + try { + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + fail("Invalid packet parsed successfully: " + offerPacket); + } catch (ParseException expected) { + } return; } + + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + assertNotNull(offerPacket); assertEquals(rawLeaseTime, offerPacket.mLeaseTime); DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash. assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis()); @@ -200,14 +208,14 @@ public class DhcpPacketTest extends TestCase { } private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes); checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes); } private void checkIpAddress(String expected, byte type, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp) .setNetmaskBytes(netmaskBytes) .build(); @@ -506,4 +514,74 @@ public class DhcpPacketTest extends TestCase { assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults); } + + @SmallTest + public void testUdpServerAnySourcePort() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "9cd917000000001c2e0000000800" + + // IP header. + "45a00148000040003d115087d18194fb0a0f7af2" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + // NOTE: The server source port is not the canonical port 67. + "C29F004401341268" + + // BOOTP header. + "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" + + // MAC address. + "9cd91700000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + + "d18180060f0777766d2e6564751c040a0fffffff000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("10.15.122.242/16", "10.15.200.23", + "209.129.128.3,209.129.148.3,209.129.128.6", + "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults); + } + + @SmallTest + public void testMultipleRouters() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "fc3d93000000" + "081735000000" + "0800" + + // IP header. + "45000148c2370000ff117ac2c0a8bd02ffffffff" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + "0043004401343beb" + + // BOOTP header. + "0201060027f518e20000800000000000c0a8bd310000000000000000" + + // MAC address. + "fc3d9300000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" + + "0308c0a8bd01ffffff0006080808080808080404ff000000000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", + null, "192.171.189.2", null, 28800, false, dhcpResults); + } } diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java new file mode 100644 index 0000000..be6861c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java @@ -0,0 +1,64 @@ +package com.android.server.content; + +import android.os.Bundle; + +import junit.framework.TestCase; + +public class SyncManagerTest extends TestCase { + + final String KEY_1 = "key_1"; + final String KEY_2 = "key_2"; + + public void testSyncExtrasEquals_WithNull() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + assertTrue("Null extra not properly compared between bundles.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsBigger_WithNull() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + b1.putString(KEY_2, "bla"); + b2.putString(KEY_2, "bla"); + + assertTrue("Extras not properly compared between bundles.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsFails_differentValues() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + b1.putString(KEY_2, "bla"); + b2.putString(KEY_2, "ble"); // different key + + assertFalse("Extras considered equal when they are different.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsFails_differentNulls() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, "bla"); // different key + + b1.putString(KEY_2, "ble"); + b2.putString(KEY_2, "ble"); + + assertFalse("Extras considered equal when they are different.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index bd64392..0b73beb 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -6,6 +6,7 @@ import android.content.Context; import android.app.job.JobInfo; import android.app.job.JobInfo.Builder; import android.os.PersistableBundle; +import android.os.SystemClock; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.util.Log; @@ -102,6 +103,14 @@ public class JobStoreTest extends AndroidTestCase { Iterator<JobStatus> it = jobStatusSet.iterator(); JobStatus loaded1 = it.next(); JobStatus loaded2 = it.next(); + + // Reverse them so we know which comparison to make. + if (loaded1.getJobId() != 8) { + JobStatus tmp = loaded1; + loaded1 = loaded2; + loaded2 = tmp; + } + assertTasksEqual(task1, loaded1.getJob()); assertTasksEqual(task2, loaded2.getJob()); assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1)); @@ -143,6 +152,36 @@ public class JobStoreTest extends AndroidTestCase { assertTasksEqual(task, loaded.getJob()); } + public void testMassivePeriodClampedOnRead() throws Exception { + final long TEN_SECONDS = 10000L; + JobInfo.Builder b = new Builder(8, mComponent) + .setPeriodic(TEN_SECONDS) + .setPersisted(true); + final long invalidLateRuntimeElapsedMillis = + SystemClock.elapsedRealtime() + (TEN_SECONDS * 2) + 5000; // >2P from now. + final long invalidEarlyRuntimeElapsedMillis = + invalidLateRuntimeElapsedMillis - TEN_SECONDS; // Early is (late - period). + final JobStatus js = new JobStatus(b.build(), SOME_UID, + invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis); + + mTaskStoreUnderTest.add(js); + Thread.sleep(IO_WAIT); + + final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.iterator().next(); + + // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll + // call SystemClock.elapsedRealtime after doing the disk i/o. + final long newNowElapsed = SystemClock.elapsedRealtime(); + assertTrue("Early runtime wasn't correctly clamped.", + loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS); + // Assert late runtime was clamped to be now + period*2. + assertTrue("Early runtime wasn't correctly clamped.", + loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2); + } + /** * Helper function to throw an error if the provided task and TaskStatus objects are not equal. */ diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index c518f77..54d9cd9 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -314,6 +314,8 @@ public class UsageStatsService extends SystemService implements mAppIdleParoled = paroled; if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled); if (paroled) { + postParoleEndTimeout(); + } else { mLastAppIdleParoledTime = checkAndGetTimeLocked(); postNextParoleTimeout(); } @@ -383,9 +385,11 @@ public class UsageStatsService extends SystemService implements timeNow); final int packageCount = packages.size(); for (int p = 0; p < packageCount; p++) { - final String packageName = packages.get(p).packageName; - final boolean isIdle = isAppIdleFiltered(packageName, userId, service, timeNow, - screenOnTime); + final PackageInfo pi = packages.get(p); + final String packageName = pi.packageName; + final boolean isIdle = isAppIdleFiltered(packageName, + UserHandle.getAppId(pi.applicationInfo.uid), + userId, service, timeNow, screenOnTime); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, isIdle ? 1 : 0, packageName)); mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow); @@ -404,8 +408,6 @@ public class UsageStatsService extends SystemService implements if (timeSinceLastParole > mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.d(TAG, "Crossed default parole interval"); setAppIdleParoled(true); - // Make sure it ends at some point - postParoleEndTimeout(); } else { if (DEBUG) Slog.d(TAG, "Not long enough to go to parole"); postNextParoleTimeout(); @@ -492,7 +494,6 @@ public class UsageStatsService extends SystemService implements if (!deviceIdle && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); - postNextParoleTimeout(); setAppIdleParoled(true); } else if (deviceIdle) { if (DEBUG) Slog.i(TAG, "Device idle, back to prison"); @@ -770,10 +771,17 @@ public class UsageStatsService extends SystemService implements if (mAppIdleParoled) { return false; } - return isAppIdleFiltered(packageName, userId, timeNow); + try { + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS); + return isAppIdleFiltered(packageName, ai.uid, userId, timeNow); + } catch (PackageManager.NameNotFoundException e) { + } + return false; } - boolean isAppIdleFiltered(String packageName, int userId, long timeNow) { + boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) { final UserUsageStatsService userService; final long screenOnTime; synchronized (mLock) { @@ -783,7 +791,8 @@ public class UsageStatsService extends SystemService implements userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow); screenOnTime = getScreenOnTimeLocked(timeNow); } - return isAppIdleFiltered(packageName, userId, userService, timeNow, screenOnTime); + return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId, + userService, timeNow, screenOnTime); } /** @@ -792,14 +801,22 @@ public class UsageStatsService extends SystemService implements * This happens if the device is plugged in or temporarily allowed to make exceptions. * Called by interface impls. */ - private boolean isAppIdleFiltered(String packageName, int userId, + private boolean isAppIdleFiltered(String packageName, int appId, int userId, UserUsageStatsService userService, long timeNow, long screenOnTime) { if (packageName == null) return false; // If not enabled at all, of course nobody is ever idle. if (!mAppIdleEnabled) { return false; } - if (packageName.equals("android")) return false; + if (appId < Process.FIRST_APPLICATION_UID) { + // System uids never go idle. + return false; + } + if (packageName.equals("android")) { + // Nor does the framework (which should be redundant with the above, but for MR1 we will + // retain this for safety). + return false; + } try { // We allow all whitelisted apps, including those that don't want to be whitelisted // for idle mode, because app idle (aka app standby) is really not as big an issue @@ -866,8 +883,8 @@ public class UsageStatsService extends SystemService implements ApplicationInfo ai = apps.get(i); // Check whether this app is idle. - boolean idle = isAppIdleFiltered(ai.packageName, userId, userService, timeNow, - screenOnTime); + boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), + userId, userService, timeNow, screenOnTime); int index = uidStates.indexOfKey(ai.uid); if (index < 0) { @@ -1353,8 +1370,8 @@ public class UsageStatsService extends SystemService implements } @Override - public boolean isAppIdle(String packageName, int userId) { - return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1); + public boolean isAppIdle(String packageName, int uidForAppId, int userId) { + return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1); } @Override diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index a79c8ea..34a17a2 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -730,6 +730,8 @@ public class UsbDeviceManager { if (active && mCurrentUser != UserHandle.USER_NULL) { Slog.v(TAG, "Current user switched to " + mCurrentUser + "; resetting USB host stack for MTP or PTP"); + // avoid leaking sensitive data from previous user + mUsbDataUnlocked = false; setEnabledFunctions(mCurrentFunctions, true); } mCurrentUser = msg.arg1; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java index 3ca0c84..31d859f 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java @@ -87,6 +87,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This is an indirect indication of the microphone being open in some other application. private boolean mServiceDisabled = false; private boolean mStarted = false; + private boolean mRecognitionAborted = false; private PowerSaveModeListener mPowerSaveModeListener; SoundTriggerHelper(Context context) { @@ -386,8 +387,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private void onRecognitionAbortLocked() { Slog.w(TAG, "Recognition aborted"); - // No-op - // This is handled via service state changes instead. + // If abort has been called, the hardware has already stopped recognition, so we shouldn't + // call it again when we process the state change. + mRecognitionAborted = true; } private void onRecognitionFailureLocked() { @@ -490,8 +492,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } return status; } else { - // Stop recognition. - int status = mModule.stopRecognition(mCurrentSoundModelHandle); + // Stop recognition (only if we haven't been aborted). + int status = STATUS_OK; + if (!mRecognitionAborted) { + status = mModule.stopRecognition(mCurrentSoundModelHandle); + } else { + mRecognitionAborted = false; + } if (status != SoundTrigger.STATUS_OK) { Slog.w(TAG, "stopRecognition call failed with " + status); if (notify) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a8874d0..a96c164 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -925,7 +925,7 @@ public class VoiceInteractionManagerService extends SystemService { return; } synchronized (this) { - pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); + pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)"); pw.println(" mEnableService: " + mEnableService); if (mImpl == null) { pw.println(" (No active implementation)"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 28520be..30296e1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,6 +36,7 @@ import android.service.voice.IVoiceInteractionService; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionServiceInfo; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.IWindowManager; @@ -114,9 +115,9 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne mAm = ActivityManagerNative.getDefault(); VoiceInteractionServiceInfo info; try { - info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Voice interaction service not found: " + service); + info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); + } catch (RemoteException|PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Voice interaction service not found: " + service, e); mInfo = null; mSessionComponentName = null; mIWindowManager = null; @@ -260,9 +261,18 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne } return; } + pw.print(" mUser="); pw.println(mUser); pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); pw.print(" Session service="); pw.println(mInfo.getSessionService()); + pw.println(" Service info:"); + mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " "); + pw.println(" Application info:"); + mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), " "); + pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService()); pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); + pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist()); + pw.print(" Supports launch from keyguard="); + pw.println(mInfo.getSupportsLaunchFromKeyguard()); if (mDisabledShowContext != 0) { pw.print(" mDisabledShowContext="); pw.println(Integer.toHexString(mDisabledShowContext)); |