diff options
Diffstat (limited to 'services')
172 files changed, 5143 insertions, 3085 deletions
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 9b863a9f2d26..61f63d3bbc3d 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -47,7 +47,7 @@ final class RemoteAugmentedAutofillService private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; + private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, @@ -106,6 +106,12 @@ final class RemoteAugmentedAutofillService activityComponent, focusedId, focusedValue)); } + @Override + public String toString() { + return "RemoteAugmentedAutofillService[" + + ComponentName.flattenToShortString(getComponentName()) + "]"; + } + /** * Called by {@link Session} when it's time to destroy all augmented autofill requests. */ @@ -181,11 +187,13 @@ final class RemoteAugmentedAutofillService @Override protected void onTimeout(RemoteAugmentedAutofillService remoteService) { - Slog.wtf(TAG, "timed out: " + this); + // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the + // timeout + Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS + + "ms) for " + remoteService); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks finish(); } - } public interface RemoteAugmentedAutofillServiceCallbacks diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 17e9b350d368..00cb6d3a0a26 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -47,6 +47,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; +import com.android.server.backup.utils.FileUtils; +import com.android.server.backup.utils.RandomAccessFileUtils; import java.io.File; import java.io.FileDescriptor; @@ -89,6 +91,12 @@ public class Trampoline extends IBackupManager.Stub { */ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated"; + /** + * Name of file for non-system users that remembers whether backup was explicitly activated or + * deactivated with a call to setBackupServiceActive. + */ + private static final String REMEMBER_ACTIVATED_FILENAME_PREFIX = "backup-remember-activated"; + // Product-level suppression of backup/restore. private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable"; @@ -134,11 +142,17 @@ public class Trampoline extends IBackupManager.Stub { } /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return FileUtils.createNewFile(UserBackupManagerFiles.getStateFileInSystemDir( + REMEMBER_ACTIVATED_FILENAME_PREFIX, userId)); + } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ protected File getActivatedFileForNonSystemUser(int userId) { - return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM), - BACKUP_ACTIVATED_FILENAME + "-" + userId); + return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId); } + // TODO (b/124359804) move to util method in FileUtils private void createFile(File file) throws IOException { if (file.exists()) { return; @@ -150,6 +164,7 @@ public class Trampoline extends IBackupManager.Stub { } } + // TODO (b/124359804) move to util method in FileUtils private void deleteFile(File file) { if (!file.exists()) { return; @@ -312,6 +327,19 @@ public class Trampoline extends IBackupManager.Stub { public void setBackupServiceActive(int userId, boolean makeActive) { enforcePermissionsOnUser(userId); + // In Q, backup is OFF by default for non-system users. In the future, we will change that + // to ON unless backup was explicitly deactivated with a (permissioned) call to + // setBackupServiceActive. + // Therefore, remember this for use in the future. Basically the default in the future will + // be: rememberFile.exists() ? rememberFile.value() : ON + // Note that this has to be done right after the permission checks and before any other + // action since we need to remember that a permissioned call was made irrespective of + // whether the call changes the state or not. + if (userId != UserHandle.USER_SYSTEM) { + RandomAccessFileUtils.writeBoolean(getRememberActivatedFileForNonSystemUser(userId), + makeActive); + } + if (mGlobalDisable) { Slog.i(TAG, "Backup service not supported"); return; diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java index aabd41a611a1..4638ac63de4a 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java @@ -48,4 +48,9 @@ final class UserBackupManagerFiles { // is a staging dir, we dont need to copy below dir to new system user dir return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR); } + + /** Stored in the system user's directory and the file is indexed by the user it refers to. */ + static File getStateFileInSystemDir(String prefix, int userId) { + return new File(getBaseStateDir(UserHandle.USER_SYSTEM), prefix + "-" + userId); + } } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index b2afbc3ec5f9..32e2cacbb37b 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -70,6 +70,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; @@ -130,6 +131,7 @@ import com.android.server.backup.transport.TransportNotRegisteredException; import com.android.server.backup.utils.AppBackupUtils; import com.android.server.backup.utils.BackupManagerMonitorUtils; import com.android.server.backup.utils.BackupObserverUtils; +import com.android.server.backup.utils.FileUtils; import com.android.server.backup.utils.SparseArrayUtils; import com.google.android.collect.Sets; @@ -1483,19 +1485,50 @@ public class UserBackupManagerService { } /** - * Clear an application's data, blocking until the operation completes or times out. If {@code - * keepSystemState} is {@code true}, we intentionally do not clear system state that would - * ordinarily also be cleared, because we aren't actually wiping the app back to empty; we're - * bringing it into the actual expected state related to the already-restored notification state - * etc. + * Clear an application's data after a failed restore, blocking until the operation completes or + * times out. */ - public void clearApplicationDataSynchronous(String packageName, boolean keepSystemState) { - // Don't wipe packages marked allowClearUserData=false + public void clearApplicationDataAfterRestoreFailure(String packageName) { + clearApplicationDataSynchronous(packageName, true, false); + } + + /** + * Clear an application's data before restore, blocking until the operation completes or times + * out. + */ + public void clearApplicationDataBeforeRestore(String packageName) { + clearApplicationDataSynchronous(packageName, false, true); + } + + /** + * Clear an application's data, blocking until the operation completes or times out. + * + * @param checkFlagAllowClearUserDataOnFailedRestore if {@code true} uses + * {@link ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE} to decide if + * clearing data is allowed after a failed restore. + * + * @param keepSystemState if {@code true}, we don't clear system state such as already restored + * notification settings, permission grants, etc. + */ + private void clearApplicationDataSynchronous(String packageName, + boolean checkFlagAllowClearUserDataOnFailedRestore, boolean keepSystemState) { try { - PackageInfo info = mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId); - if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) { + ApplicationInfo applicationInfo = mPackageManager.getPackageInfoAsUser( + packageName, 0, mUserId).applicationInfo; + + boolean shouldClearData; + if (checkFlagAllowClearUserDataOnFailedRestore + && applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) { + shouldClearData = (applicationInfo.privateFlags + & ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE) != 0; + } else { + shouldClearData = + (applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) != 0; + } + + if (!shouldClearData) { if (MORE_DEBUG) { - Slog.i(TAG, "allowClearUserData=false so not wiping " + Slog.i(TAG, "Clearing app data is not allowed so not wiping " + packageName); } return; @@ -1510,8 +1543,8 @@ public class UserBackupManagerService { synchronized (mClearDataLock) { mClearingData = true; try { - mActivityManager.clearApplicationUserData( - packageName, keepSystemState, observer, mUserId); + mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer, + mUserId); } catch (RemoteException e) { // can't happen because the activity manager is in this process } @@ -2319,6 +2352,7 @@ public class UserBackupManagerService { mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "setAncestralSerialNumber"); Slog.v(TAG, "Setting ancestral work profile id to " + ancestralSerialNumber); + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { af.writeLong(ancestralSerialNumber); } catch (IOException e) { @@ -2331,6 +2365,7 @@ public class UserBackupManagerService { * {@link #setAncestralSerialNumber(long)}. Will return {@code -1} if not set. */ public long getAncestralSerialNumber() { + // TODO (b/124359804) try (RandomAccessFile af = getAncestralSerialNumberFile()) { return af.readLong(); } catch (IOException e) { @@ -2344,13 +2379,7 @@ public class UserBackupManagerService { mAncestralSerialNumberFile = new File( UserBackupManagerFiles.getBaseStateDir(getUserId()), SERIAL_ID_FILE); - if (!mAncestralSerialNumberFile.exists()) { - try { - mAncestralSerialNumberFile.createNewFile(); - } catch (IOException e) { - Slog.w(TAG, "serial number mapping file creation failed", e); - } - } + FileUtils.createNewFile(mAncestralSerialNumberFile); } return new RandomAccessFile(mAncestralSerialNumberFile, "rwd"); } diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java index c5389fa5f878..836a5e883c62 100644 --- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java +++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java @@ -352,7 +352,7 @@ public class FullRestoreEngine extends RestoreEngine { Slog.d(TAG, "Clearing app data preparatory to full restore"); } - mBackupManagerService.clearApplicationDataSynchronous(pkg, true); + mBackupManagerService.clearApplicationDataBeforeRestore(pkg); } else { if (MORE_DEBUG) { Slog.d(TAG, "backup agent (" diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java index 324c2d974010..6714b0aea261 100644 --- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java +++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java @@ -660,7 +660,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { ++mCount; } catch (Exception e) { Slog.e(TAG, "Error when attempting restore: " + e.toString()); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(false); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } } @@ -686,6 +686,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { boolean staging = !packageName.equals("android"); ParcelFileDescriptor stage; File downloadFile = (staging) ? mStageName : mBackupDataName; + boolean startedAgentRestore = false; try { IBackupTransport transport = @@ -766,13 +767,15 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(); backupManagerService.prepareOperationTimeout( mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT); + startedAgentRestore = true; mAgent.doRestore(mBackupData, appVersionCode, mNewState, mEphemeralOpToken, backupManagerService.getBackupManagerBinder()); } catch (Exception e) { Slog.e(TAG, "Unable to call app for restore: " + packageName, e); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); - keyValueAgentErrorCleanup(); // clears any pending timeout messages as well + // Clears any pending timeout messages as well. + keyValueAgentErrorCleanup(startedAgentRestore); // After a restore failure we go back to running the queue. If there // are no more packages to be restored that will be handled by the @@ -832,7 +835,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { Slog.e(TAG, "Unable to finalize restore of " + packageName); EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString()); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } } @@ -988,8 +991,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // We also need to wipe the current target's data, as it's probably // in an incoherent state. - backupManagerService.clearApplicationDataSynchronous( - mCurrentPackage.packageName, false); + backupManagerService.clearApplicationDataAfterRestoreFailure( + mCurrentPackage.packageName); // Schedule the next state based on the nature of our failure if (status == BackupTransport.TRANSPORT_ERROR) { @@ -1110,11 +1113,18 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { mListener.onFinished(callerLogString); } - void keyValueAgentErrorCleanup() { - // If the agent fails restore, it might have put the app's data - // into an incoherent state. For consistency we wipe its data - // again in this case before continuing with normal teardown - backupManagerService.clearApplicationDataSynchronous(mCurrentPackage.packageName, false); + /** + * @param clearAppData - set to {@code true} if the backup agent had already been invoked when + * restore faied. So the app data may be in corrupted state and has to be cleared. + */ + void keyValueAgentErrorCleanup(boolean clearAppData) { + if (clearAppData) { + // If the agent fails restore, it might have put the app's data + // into an incoherent state. For consistency we wipe its data + // again in this case before continuing with normal teardown + backupManagerService.clearApplicationDataAfterRestoreFailure( + mCurrentPackage.packageName); + } keyValueAgentCleanup(); } @@ -1251,7 +1261,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // Some kind of horrible semantic error; we're in an unexpected state. // Back off hard and wind up. Slog.e(TAG, "Unexpected restore callback into state " + mState); - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); nextState = UnifiedRestoreState.FINAL; break; } @@ -1271,7 +1281,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, mCurrentPackage.packageName, "restore timeout"); // Handle like an agent that threw on invocation: wipe it and go on to the next - keyValueAgentErrorCleanup(); + keyValueAgentErrorCleanup(true); executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } diff --git a/services/backup/java/com/android/server/backup/utils/FileUtils.java b/services/backup/java/com/android/server/backup/utils/FileUtils.java new file mode 100644 index 000000000000..00686cba4777 --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/FileUtils.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +/** Utility methods useful for working with backup related files. */ +public final class FileUtils { + /** + * Ensure that the file exists in the file system. If an IOException is thrown, it is ignored. + * This method is useful to avoid code duplication of the "try-catch-ignore exception" block. + */ + public static File createNewFile(File file) { + try { + file.createNewFile(); + } catch (IOException e) { + Slog.w(TAG, "Failed to create file:" + file.getAbsolutePath(), e); + } + return file; + } +} diff --git a/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java new file mode 100644 index 000000000000..abf906aee5dd --- /dev/null +++ b/services/backup/java/com/android/server/backup/utils/RandomAccessFileUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.android.server.backup.BackupManagerService.TAG; + +import android.util.Slog; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** Utility methods useful for working with backup related RandomAccessFiles. */ +public final class RandomAccessFileUtils { + private static RandomAccessFile getRandomAccessFile(File file) throws FileNotFoundException { + return new RandomAccessFile(file, "rwd"); + } + + /** Write a boolean to a File by wrapping it using a RandomAccessFile. */ + public static void writeBoolean(File file, boolean b) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + af.writeBoolean(b); + } catch (IOException e) { + Slog.w(TAG, "Error writing file:" + file.getAbsolutePath(), e); + } + } + + /** Read a boolean from a File by wrapping it using a RandomAccessFile. */ + public static boolean readBoolean(File file, boolean def) { + try (RandomAccessFile af = getRandomAccessFile(file)) { + return af.readBoolean(); + } catch (IOException e) { + Slog.w(TAG, "Error reading file:" + file.getAbsolutePath(), e); + } + return def; + } +} diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 4afbc641ea6c..45ceeda689f7 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -45,6 +45,7 @@ import android.provider.Settings; import android.util.LocalLog; import android.util.Slog; import android.util.SparseBooleanArray; +import android.view.contentcapture.ContentCaptureHelper; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.UserDataRemovalRequest; @@ -79,7 +80,8 @@ public final class ContentCaptureManagerService extends private final LocalService mLocalService = new LocalService(); - private final LocalLog mRequestsHistory = new LocalLog(20); + @Nullable + final LocalLog mRequestsHistory; @GuardedBy("mLock") private ActivityManagerInternal mAm; @@ -105,15 +107,19 @@ public final class ContentCaptureManagerService extends UserManager.DISALLOW_CONTENT_CAPTURE); DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ActivityThread.currentApplication().getMainExecutor(), - (namespace, key, value) -> { - if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED - .equals(key)) { - Slog.i(mTag, "Ignoring change on " + key); - return; - } - setDisabledByDeviceConfig(value); - }); - setDisabledByDeviceConfig(); + (namespace, key, value) -> onDeviceConfigChange(key, value)); + setLoggingLevelFromDeviceConfig(); + setDisabledFromDeviceConfig(); + + final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); + if (loggingSize > 0) { + if (debug) Slog.d(mTag, "log history size: " + loggingSize); + mRequestsHistory = new LocalLog(loggingSize); + } else { + if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize); + mRequestsHistory = null; + } // Sets which services are disabled final UserManager um = getContext().getSystemService(UserManager.class); @@ -213,7 +219,33 @@ public final class ContentCaptureManagerService extends return false; } - private void setDisabledByDeviceConfig() { + private void onDeviceConfigChange(@NonNull String key, @Nullable String value) { + switch (key) { + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED: + setDisabledByDeviceConfig(value); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL: + setLoggingLevelFromDeviceConfig(); + return; + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: + // TODO(b/123096662): implement it + Slog.d(mTag, "changes on " + key + " not supported yet"); + return; + default: + Slog.i(mTag, "Ignoring change on " + key); + } + } + + private void setLoggingLevelFromDeviceConfig() { + ContentCaptureHelper.setLoggingLevel(); + verbose = ContentCaptureHelper.sVerbose; + debug = ContentCaptureHelper.sDebug; + } + + private void setDisabledFromDeviceConfig() { final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); setDisabledByDeviceConfig(value); @@ -327,13 +359,6 @@ public final class ContentCaptureManagerService extends } } - /** - * Logs a request so it's dumped later... - */ - void logRequestLocked(@NonNull String historyItem) { - mRequestsHistory.log(historyItem); - } - private ActivityManagerInternal getAmInternal() { synchronized (mLock) { if (mAm == null) { @@ -480,31 +505,6 @@ public final class ContentCaptureManagerService extends } @Override - public void setContentCaptureFeatureEnabled(boolean enabled, - @NonNull IResultReceiver result) { - final int userId = UserHandle.getCallingUserId(); - final boolean isService; - synchronized (mLock) { - isService = assertCalledByServiceLocked("setContentCaptureFeatureEnabled()", userId, - Binder.getCallingUid(), result); - } - if (!isService) return; - - final long token = Binder.clearCallingIdentity(); - try { - Settings.Secure.putStringForUser(getContext().getContentResolver(), - Settings.Secure.CONTENT_CAPTURE_ENABLED, Boolean.toString(enabled), userId); - } finally { - Binder.restoreCallingIdentity(token); - } - try { - result.send(ContentCaptureManager.RESULT_CODE_TRUE, /* resultData= */null); - } catch (RemoteException e) { - Slog.w(mTag, "Unable to send setContentCaptureFeatureEnabled(): " + e); - } - } - - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return; @@ -527,9 +527,13 @@ public final class ContentCaptureManagerService extends synchronized (mLock) { dumpLocked("", pw); } - if (showHistory) { - pw.println(); pw.println("Requests history:"); pw.println(); + pw.print("Requests history: "); + if (mRequestsHistory == null) { + pw.println("disabled by device config"); + } else if (showHistory) { + pw.println(); mRequestsHistory.reverseDump(fd, pw, args); + pw.println(); } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 7102b82d5e18..9e159600e42c 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -21,6 +21,7 @@ import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED; import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID; import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR; import static android.view.contentcapture.ContentCaptureSession.STATE_NO_SERVICE; +import static android.view.contentcapture.ContentCaptureSession.STATE_PACKAGE_NOT_WHITELISTED; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; @@ -43,11 +44,12 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; import android.util.ArrayMap; -import android.util.Log; +import android.util.ArraySet; import android.util.Slog; import android.view.contentcapture.UserDataRemovalRequest; @@ -87,6 +89,12 @@ final class ContentCapturePerUserService private final ContentCaptureServiceRemoteCallback mRemoteServiceCallback = new ContentCaptureServiceRemoteCallback(); + /** + * List of packages that are whitelisted to be content captured. + */ + @GuardedBy("mLock") + private final ArraySet<String> mWhitelistedPackages = new ArraySet<>(); + // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's ContentCapturePerUserService(@NonNull ContentCaptureManagerService master, @@ -185,15 +193,19 @@ final class ContentCapturePerUserService final int taskId = activityPresentationInfo.taskId; final int displayId = activityPresentationInfo.displayId; final ComponentName componentName = activityPresentationInfo.componentName; + final boolean whitelisted = isWhitelistedLocked(componentName); final ComponentName serviceComponentName = getServiceComponentName(); final boolean enabled = isEnabledLocked(); - final String historyItem = - "id=" + sessionId + " uid=" + uid - + " a=" + ComponentName.flattenToShortString(componentName) - + " t=" + taskId + " d=" + displayId - + " s=" + ComponentName.flattenToShortString(serviceComponentName) - + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)"); - mMaster.logRequestLocked(historyItem); + if (mMaster.mRequestsHistory != null) { + final String historyItem = + "id=" + sessionId + " uid=" + uid + + " a=" + ComponentName.flattenToShortString(componentName) + + " t=" + taskId + " d=" + displayId + + " s=" + ComponentName.flattenToShortString(serviceComponentName) + + " u=" + mUserId + " f=" + flags + (enabled ? "" : " (disabled)") + + " w=" + whitelisted; + mMaster.mRequestsHistory.log(historyItem); + } if (!enabled) { // TODO: it would be better to split in differet reasons, like @@ -212,6 +224,16 @@ final class ContentCapturePerUserService return; } + if (!whitelisted) { + if (mMaster.debug) { + Slog.d(TAG, "startSession(" + componentName + "): not whitelisted"); + } + // TODO(b/122595322): need to return STATE_ACTIVITY_NOT_WHITELISTED as well + setClientState(clientReceiver, STATE_DISABLED | STATE_PACKAGE_NOT_WHITELISTED, + /* binder= */ null); + return; + } + final ContentCaptureServerSession existingSession = mSessions.get(sessionId); if (existingSession != null) { Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken @@ -245,6 +267,26 @@ final class ContentCapturePerUserService newSession.notifySessionStartedLocked(clientReceiver); } + @GuardedBy("mLock") + private boolean isWhitelistedLocked(@NonNull ComponentName componentName) { + // TODO(b/122595322): need to check whitelisted activities as well. + final String packageName = componentName.getPackageName(); + return mWhitelistedPackages.contains(packageName); + } + + private void whitelistPackages(@NonNull List<String> packages) { + // TODO(b/122595322): add CTS test for when it's null + synchronized (mLock) { + if (packages == null) { + if (mMaster.verbose) Slog.v(TAG, "clearing all whitelisted packages"); + mWhitelistedPackages.clear(); + } else { + if (mMaster.verbose) Slog.v(TAG, "whitelisting packages: " + packages); + mWhitelistedPackages.addAll(packages); + } + } + } + // TODO(b/119613670): log metrics @GuardedBy("mLock") public void finishSessionLocked(@NonNull String sessionId) { @@ -376,15 +418,23 @@ final class ContentCapturePerUserService mRemoteService.dump(prefix2, pw); } + final int whitelistSize = mWhitelistedPackages.size(); + pw.print(prefix); pw.print("Whitelisted packages: "); pw.println(whitelistSize); + for (int i = 0; i < whitelistSize; i++) { + final String whitelistedPkg = mWhitelistedPackages.valueAt(i); + pw.print(prefix2); pw.print(i + 1); pw.print(": "); pw.println(whitelistedPkg); + } + if (mSessions.isEmpty()) { pw.print(prefix); pw.println("no sessions"); } else { - final int size = mSessions.size(); - pw.print(prefix); pw.print("number sessions: "); pw.println(size); - for (int i = 0; i < size; i++) { - pw.print(prefix); pw.print("session@"); pw.println(i); + final int sessionsSize = mSessions.size(); + pw.print(prefix); pw.print("number sessions: "); pw.println(sessionsSize); + for (int i = 0; i < sessionsSize; i++) { + pw.print(prefix); pw.print("#"); pw.println(i); final ContentCaptureServerSession session = mSessions.valueAt(i); session.dumpLocked(prefix2, pw); + pw.println(); } } } @@ -410,11 +460,26 @@ final class ContentCapturePerUserService public void setContentCaptureWhitelist(List<String> packages, List<ComponentName> activities) { if (mMaster.verbose) { - Log.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + Slog.v(TAG, "setContentCaptureWhitelist(packages=" + packages + ", activities=" + activities + ")"); } - // TODO(b/122595322): implement + whitelistPackages(packages); + + // TODO(b/122595322): whitelist activities as well // TODO(b/119613670): log metrics } + + @Override + public void disableSelf() { + if (mMaster.verbose) Slog.v(TAG, "disableSelf()"); + + final long token = Binder.clearCallingIdentity(); + try { + Settings.Secure.putStringForUser(getContext().getContentResolver(), + Settings.Secure.CONTENT_CAPTURE_ENABLED, "false", mUserId); + } finally { + Binder.restoreCallingIdentity(token); + } + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java index 3c52e17ce1e8..40948432751d 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.os.IBinder; import android.service.contentcapture.ContentCaptureService; import android.service.contentcapture.SnapshotData; +import android.util.LocalLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureSessionId; @@ -37,6 +38,9 @@ final class ContentCaptureServerSession { final IBinder mActivityToken; private final ContentCapturePerUserService mService; private final RemoteContentCaptureService mRemoteService; + + // NOTE: this is the "internal" context (like package and taskId), not the explicit content + // set by apps - those are only send to the ContentCaptureService. private final ContentCaptureContext mContentCaptureContext; /** @@ -83,7 +87,10 @@ final class ContentCaptureServerSession { */ @GuardedBy("mLock") public void sendActivitySnapshotLocked(@NonNull SnapshotData snapshotData) { - mService.getMaster().logRequestLocked("snapshot: id=" + mId); + final LocalLog logHistory = mService.getMaster().mRequestsHistory; + if (logHistory != null) { + logHistory.log("snapshot: id=" + mId); + } mRemoteService.onActivitySnapshotRequest(mId, snapshotData); } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index d1cd072ee215..915c131ff0fb 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -57,8 +57,10 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.database.ContentObserver; +import android.net.CaptivePortal; import android.net.ConnectionInfo; import android.net.ConnectivityManager; +import android.net.ICaptivePortal; import android.net.IConnectivityManager; import android.net.IIpConnectivityMetrics; import android.net.INetd; @@ -86,6 +88,7 @@ import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.NetworkStack; +import android.net.NetworkStackClient; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.NetworkWatchlistManager; @@ -917,7 +920,8 @@ public class ConnectivityService extends IConnectivityManager.Stub mPermissionMonitor = new PermissionMonitor(mContext, mNMS); - //set up the listener for user state for creating user VPNs + // Set up the listener for user state for creating user VPNs. + // Should run on mHandler to avoid any races. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_STARTED); intentFilter.addAction(Intent.ACTION_USER_STOPPED); @@ -925,7 +929,11 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser( - mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, + UserHandle.ALL, + intentFilter, + null /* broadcastPermission */, + mHandler); mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM, new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); @@ -936,7 +944,11 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); intentFilter.addDataScheme("package"); mContext.registerReceiverAsUser( - mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, + UserHandle.ALL, + intentFilter, + null /* broadcastPermission */, + mHandler); try { mNMS.registerObserver(mTethering); @@ -2690,11 +2702,6 @@ public class ConnectivityService extends IConnectivityManager.Stub EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, mNai.network.netId)); } - - @Override - public void logCaptivePortalLoginEvent(int eventId, String packageName) { - new MetricsLogger().action(eventId, packageName); - } } private boolean networkRequiresValidation(NetworkAgentInfo nai) { @@ -2842,6 +2849,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) { log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests()); } + // Clear all notifications of this network. + mNotifier.clearNotification(nai.network.netId); // A network agent has disconnected. // TODO - if we move the logic to the network agent (have them disconnect // because they lost all their requests or because their score isn't good) @@ -3247,22 +3256,63 @@ public class ConnectivityService extends IConnectivityManager.Stub /** * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself. + * @param network Network on which the captive portal was detected. * @param appExtras Bundle to use as intent extras for the captive portal application. * Must be treated as opaque to avoid preventing the captive portal app to * update its arguments. */ @Override - public void startCaptivePortalAppInternal(Bundle appExtras) { + public void startCaptivePortalAppInternal(Network network, Bundle appExtras) { mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN); appIntent.putExtras(appExtras); + appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL, + new CaptivePortal(new CaptivePortalImpl(network).asBinder())); appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK); Binder.withCleanCallingIdentity(() -> mContext.startActivityAsUser(appIntent, UserHandle.CURRENT)); } + private class CaptivePortalImpl extends ICaptivePortal.Stub { + private final Network mNetwork; + + private CaptivePortalImpl(Network network) { + mNetwork = network; + } + + @Override + public void appResponse(final int response) throws RemoteException { + if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) { + enforceSettingsPermission(); + } + + // getNetworkAgentInfoForNetwork is thread-safe + final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork); + if (nai == null) return; + + // nai.networkMonitor() is thread-safe + final INetworkMonitor nm = nai.networkMonitor(); + if (nm == null) return; + + final long token = Binder.clearCallingIdentity(); + try { + nm.notifyCaptivePortalAppFinished(response); + } finally { + // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void logEvent(int eventId, String packageName) { + enforceSettingsPermission(); + + new MetricsLogger().action(eventId, packageName); + } + } + public boolean avoidBadWifi() { return mMultinetworkPolicyTracker.getAvoidBadWifi(); } @@ -4097,17 +4147,27 @@ public class ConnectivityService extends IConnectivityManager.Stub * handler thread through their agent, this is asynchronous. When the capabilities objects * are computed they will be up-to-date as they are computed synchronously from here and * this is running on the ConnectivityService thread. - * TODO : Fix this and call updateCapabilities inline to remove out-of-order events. */ private void updateAllVpnsCapabilities() { + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { for (int i = 0; i < mVpns.size(); i++) { final Vpn vpn = mVpns.valueAt(i); - vpn.updateCapabilities(); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } + private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) { + ensureRunningOnConnectivityServiceThread(); + NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId()); + if (vpnNai == null || nc == null) { + return; + } + updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc); + } + @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { @@ -4448,22 +4508,28 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(int userId) { mPermissionMonitor.onUserAdded(userId); + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserAdded(userId); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } private void onUserRemoved(int userId) { mPermissionMonitor.onUserRemoved(userId); + Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserRemoved(userId); + NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); + updateVpnCapabilities(vpn, nc); } } } @@ -4532,6 +4598,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + ensureRunningOnConnectivityServiceThread(); final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -5041,6 +5108,19 @@ public class ConnectivityService extends IConnectivityManager.Stub return getNetworkForRequest(mDefaultRequest.requestId); } + @Nullable + private Network getNetwork(@Nullable NetworkAgentInfo nai) { + return nai != null ? nai.network : null; + } + + private void ensureRunningOnConnectivityServiceThread() { + if (mHandler.getLooper().getThread() != Thread.currentThread()) { + throw new IllegalStateException( + "Not running on ConnectivityService thread: " + + Thread.currentThread().getName()); + } + } + private boolean isDefaultNetwork(NetworkAgentInfo nai) { return nai == getDefaultNetwork(); } @@ -5097,7 +5177,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("registerNetworkAgent " + nai); final long token = Binder.clearCallingIdentity(); try { - mContext.getSystemService(NetworkStack.class).makeNetworkMonitor( + getNetworkStack().makeNetworkMonitor( toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai)); } finally { Binder.restoreCallingIdentity(token); @@ -5109,6 +5189,11 @@ public class ConnectivityService extends IConnectivityManager.Stub return nai.network.netId; } + @VisibleForTesting + protected NetworkStackClient getNetworkStack() { + return NetworkStackClient.getInstance(); + } + private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) { nai.onNetworkMonitorCreated(networkMonitor); if (VDBG) log("Got NetworkAgent Messenger"); @@ -5667,6 +5752,8 @@ public class ConnectivityService extends IConnectivityManager.Stub updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes()); mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers()); notifyIfacesChangedForNetworkStats(); + // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks. + updateAllVpnsCapabilities(); } private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) { @@ -6106,6 +6193,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // doing. updateSignalStrengthThresholds(networkAgent, "CONNECT", null); + if (networkAgent.isVPN()) { + updateAllVpnsCapabilities(); + } + // Consider network even though it is not yet validated. final long now = SystemClock.elapsedRealtime(); rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now); @@ -6367,7 +6458,11 @@ public class ConnectivityService extends IConnectivityManager.Stub success = mVpns.get(user).setUnderlyingNetworks(networks); } if (success) { - mHandler.post(() -> notifyIfacesChangedForNetworkStats()); + mHandler.post(() -> { + // Update VPN's capabilities based on updated underlying network set. + updateAllVpnsCapabilities(); + notifyIfacesChangedForNetworkStats(); + }); } return success; } diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index ebd4c5584fe1..775e4c8cf4ed 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -49,7 +49,7 @@ public abstract class ExtconUEventObserver extends UEventObserver { private static final String TAG = "ExtconUEventObserver"; private static final boolean LOG = false; private static final String SELINUX_POLICIES_NEED_TO_BE_CHANGED = - "This probably mean the selinux policies need to be changed."; + "This probably means the selinux policies need to be changed."; private final Map<String, ExtconInfo> mExtconInfos = new ArrayMap<>(); @@ -68,7 +68,7 @@ public abstract class ExtconUEventObserver extends UEventObserver { * Subclasses of ExtconUEventObserver should override this method to handle UEvents. * * @param extconInfo that matches the {@code DEVPATH} of {@code event} - * @param event the event + * @param event the event */ protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event); @@ -91,6 +91,9 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Returns a new list of all external connections whose name matches {@code regex}. */ public static List<ExtconInfo> getExtconInfos(@Nullable String regex) { + if (!extconExists()) { + return new ArrayList<>(0); // Always return a new list. + } Pattern p = regex == null ? null : Pattern.compile(regex); File file = new File("/sys/class/extcon"); File[] files = file.listFiles(); @@ -159,6 +162,15 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); - return extconDir.exists() && extconDir.isDirectory(); + boolean retVal = extconDir.exists() && extconDir.isDirectory(); + // TODO(b/124364409): return the correct value after selinux policy is updated. + if (retVal) { + Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir " + + extconDir.isDirectory() + + " but reporting it does not exist until selinux policies are updated." + + " see b/124364409" + ); + } + return false; } } diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java index fe30057fc820..5d0e308f6649 100644 --- a/services/core/java/com/android/server/FgThread.java +++ b/services/core/java/com/android/server/FgThread.java @@ -17,9 +17,12 @@ package com.android.server; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Looper; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Shared singleton foreground thread for the system. This is a thread for regular * foreground service operations, which shouldn't be blocked by anything running in @@ -34,6 +37,7 @@ public final class FgThread extends ServiceThread { private static FgThread sInstance; private static Handler sHandler; + private static HandlerExecutor sHandlerExecutor; private FgThread() { super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); @@ -48,6 +52,7 @@ public final class FgThread extends ServiceThread { looper.setSlowLogThresholdMs( SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -64,4 +69,11 @@ public final class FgThread extends ServiceThread { return sHandler; } } + + public static Executor getExecutor() { + synchronized (FgThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } } diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java index bfe825a3a89e..21fd29c3bbef 100644 --- a/services/core/java/com/android/server/IoThread.java +++ b/services/core/java/com/android/server/IoThread.java @@ -17,8 +17,11 @@ package com.android.server; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Shared singleton I/O thread for the system. This is a thread for non-background * service operations that can potential block briefly on network IO operations @@ -27,6 +30,7 @@ import android.os.Trace; public final class IoThread extends ServiceThread { private static IoThread sInstance; private static Handler sHandler; + private static HandlerExecutor sHandlerExecutor; private IoThread() { super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/); @@ -38,6 +42,7 @@ public final class IoThread extends ServiceThread { sInstance.start(); sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -54,4 +59,11 @@ public final class IoThread extends ServiceThread { return sHandler; } } + + public static Executor getExecutor() { + synchronized (IoThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 3479e18b97c5..5989a46c5a0a 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1645,36 +1645,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { - protected final CallerIdentity mCallerIdentity; - protected final String mListenerName; - - private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, - @NonNull String listenerName) { - mCallerIdentity = callerIdentity; - mListenerName = listenerName; - } - } - - private static class LinkedListener<TListener> extends LinkedListenerBase { - private final TListener mListener; - private final Consumer<TListener> mBinderDeathCallback; - - private LinkedListener(@NonNull TListener listener, String listenerName, - @NonNull CallerIdentity callerIdentity, - @NonNull Consumer<TListener> binderDeathCallback) { - super(callerIdentity, listenerName); - mListener = listener; - mBinderDeathCallback = binderDeathCallback; - } - - @Override - public void binderDied() { - if (D) Log.d(TAG, "Remote " + mListenerName + " died."); - mBinderDeathCallback.accept(mListener); - } - } - @Override public void removeGnssBatchingCallback() { synchronized (mLock) { @@ -2069,7 +2039,7 @@ public class LocationManagerService extends ILocationManager.Stub { } if (!provider.isUseableLocked()) { if (isSettingsExemptLocked(record)) { - providerRequest.forceLocation = true; + providerRequest.locationSettingsIgnored = true; providerRequest.lowPowerMode = false; } else { continue; @@ -2079,8 +2049,9 @@ public class LocationManagerService extends ILocationManager.Stub { LocationRequest locationRequest = record.mRealRequest; long interval = locationRequest.getInterval(); + // if we're forcing location, don't apply any throttling - if (!providerRequest.forceLocation && !isThrottlingExemptLocked( + if (!providerRequest.locationSettingsIgnored && !isThrottlingExemptLocked( record.mReceiver.mCallerIdentity)) { if (!record.mIsForegroundUid) { interval = Math.max(interval, backgroundThrottleInterval); @@ -2165,6 +2136,13 @@ public class LocationManagerService extends ILocationManager.Stub { } } + @Override + public String[] getIgnoreSettingsWhitelist() { + synchronized (mLock) { + return mIgnoreSettingsPackageWhitelist.toArray(new String[0]); + } + } + @GuardedBy("mLock") private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) { if (callerIdentity.mUid == Process.SYSTEM_UID) { @@ -2710,77 +2688,85 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean registerGnssStatusCallback(IGnssStatusListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) { - return false; - } + return addGnssDataListener(listener, packageName, "GnssStatusListener", + mGnssStatusProvider, mGnssStatusListeners, + this::unregisterGnssStatusCallback); + } - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssStatusListener> linkedListener = new LinkedListener<>(listener, - "GnssStatusListener", callerIdentity, this::unregisterGnssStatusCallback); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } + @Override + public void unregisterGnssStatusCallback(IGnssStatusListener listener) { + removeGnssDataListener(listener, mGnssStatusProvider, mGnssStatusListeners); + } - mGnssStatusListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssStatusProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + @Override + public boolean addGnssMeasurementsListener( + IGnssMeasurementsListener listener, String packageName) { + return addGnssDataListener(listener, packageName, "GnssMeasurementsListener", + mGnssMeasurementsProvider, mGnssMeasurementsListeners, + this::removeGnssMeasurementsListener); } @Override - public void unregisterGnssStatusCallback(IGnssStatusListener listener) { - if (mGnssStatusProvider == null) { - return; + public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { + removeGnssDataListener(listener, mGnssMeasurementsProvider, mGnssMeasurementsListeners); + } + + private abstract static class LinkedListenerBase implements IBinder.DeathRecipient { + protected final CallerIdentity mCallerIdentity; + protected final String mListenerName; + + private LinkedListenerBase(@NonNull CallerIdentity callerIdentity, + @NonNull String listenerName) { + mCallerIdentity = callerIdentity; + mListenerName = listenerName; } + } - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssStatusListener> linkedListener = - mGnssStatusListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssStatusProvider.removeListener(listener); + private static class LinkedListener<TListener> extends LinkedListenerBase { + private final TListener mListener; + private final Consumer<TListener> mBinderDeathCallback; + + private LinkedListener(@NonNull TListener listener, String listenerName, + @NonNull CallerIdentity callerIdentity, + @NonNull Consumer<TListener> binderDeathCallback) { + super(callerIdentity, listenerName); + mListener = listener; + mBinderDeathCallback = binderDeathCallback; + } + + @Override + public void binderDied() { + if (D) Log.d(TAG, "Remote " + mListenerName + " died."); + mBinderDeathCallback.accept(mListener); } } - @Override - public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> boolean addGnssDataListener( + TListener listener, String packageName, String listenerName, + RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners, + Consumer<TListener> binderDeathCallback) { + if (!hasGnssPermissions(packageName) || gnssDataProvider == null) { return false; } CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); - LinkedListener<IGnssMeasurementsListener> linkedListener = new LinkedListener<>(listener, - "GnssMeasurementsListener", callerIdentity, this::removeGnssMeasurementsListener); + LinkedListener<TListener> linkedListener = new LinkedListener<>(listener, + listenerName, callerIdentity, binderDeathCallback); IBinder binder = listener.asBinder(); synchronized (mLock) { if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { return false; } - mGnssMeasurementsListeners.put(binder, linkedListener); + gnssDataListeners.put(binder, linkedListener); long identity = Binder.clearCallingIdentity(); try { if (isThrottlingExemptLocked(callerIdentity) || isImportanceForeground( mActivityManager.getPackageImportance(packageName))) { - mGnssMeasurementsProvider.addListener(listener, callerIdentity); + gnssDataProvider.addListener(listener, callerIdentity); } return true; } finally { @@ -2789,25 +2775,24 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @Override - public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) { - if (mGnssMeasurementsProvider == null) { + private <TListener extends IInterface> void removeGnssDataListener( + TListener listener, RemoteListenerHelper<TListener> gnssDataProvider, + ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) { + if (gnssDataProvider == null) { return; } IBinder binder = listener.asBinder(); synchronized (mLock) { - LinkedListener<IGnssMeasurementsListener> linkedListener = - mGnssMeasurementsListeners.remove(binder); + LinkedListener<TListener> linkedListener = gnssDataListeners.remove(binder); if (linkedListener == null) { return; } unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssMeasurementsProvider.removeListener(listener); + gnssDataProvider.removeListener(listener); } } - private boolean linkToListenerDeathNotificationLocked(IBinder binder, LinkedListenerBase linkedListener) { try { @@ -2816,8 +2801,7 @@ public class LocationManagerService extends ILocationManager.Stub { } catch (RemoteException e) { // if the remote process registering the listener is already dead, just swallow the // exception and return - Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", - e); + Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e); return false; } } @@ -2830,8 +2814,7 @@ public class LocationManagerService extends ILocationManager.Stub { } catch (NoSuchElementException e) { // if the death callback isn't connected (it should be...), log error, // swallow the exception and return - Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", - e); + Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e); return false; } } @@ -2863,52 +2846,15 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean addGnssNavigationMessageListener( IGnssNavigationMessageListener listener, String packageName) { - if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) { - return false; - } - - CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(), - Binder.getCallingPid(), packageName); - LinkedListener<IGnssNavigationMessageListener> linkedListener = - new LinkedListener<>(listener, "GnssNavigationMessageListener", callerIdentity, - this::removeGnssNavigationMessageListener); - IBinder binder = listener.asBinder(); - synchronized (mLock) { - if (!linkToListenerDeathNotificationLocked(binder, linkedListener)) { - return false; - } - - mGnssNavigationMessageListeners.put(binder, linkedListener); - long identity = Binder.clearCallingIdentity(); - try { - if (isThrottlingExemptLocked(callerIdentity) - || isImportanceForeground( - mActivityManager.getPackageImportance(packageName))) { - mGnssNavigationMessageProvider.addListener(listener, callerIdentity); - } - return true; - } finally { - Binder.restoreCallingIdentity(identity); - } - } + return addGnssDataListener(listener, packageName, "GnssNavigationMessageListener", + mGnssNavigationMessageProvider, mGnssNavigationMessageListeners, + this::removeGnssNavigationMessageListener); } @Override public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) { - if (mGnssNavigationMessageProvider == null) { - return; - } - - IBinder binder = listener.asBinder(); - synchronized (mLock) { - LinkedListener<IGnssNavigationMessageListener> linkedListener = - mGnssNavigationMessageListeners.remove(binder); - if (linkedListener == null) { - return; - } - unlinkFromListenerDeathNotificationLocked(binder, linkedListener); - mGnssNavigationMessageProvider.removeListener(listener); - } + removeGnssDataListener(listener, mGnssNavigationMessageProvider, + mGnssNavigationMessageListeners); } @Override diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index f505b76178a4..dc394d0ad482 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -20,18 +20,18 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.NETWORK_STACK; import static android.Manifest.permission.SHUTDOWN; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_BLACKLIST; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_NONE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.INetd.FIREWALL_WHITELIST; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -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.FIREWALL_TYPE_BLACKLIST; -import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; @@ -1946,7 +1946,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub int numUids = 0; if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName); - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { // Close all sockets on all non-system UIDs... ranges = new UidRange[] { // TODO: is there a better way of finding all existing users? If so, we could @@ -1958,7 +1958,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); exemptUids = new int[rules.size()]; for (int i = 0; i < exemptUids.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) { exemptUids[numUids] = rules.keyAt(i); numUids++; } @@ -1980,7 +1980,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub final SparseIntArray rules = getUidFirewallRulesLR(chain); ranges = new UidRange[rules.size()]; for (int i = 0; i < ranges.length; i++) { - if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rules.valueAt(i) == FIREWALL_RULE_DENY) { int uid = rules.keyAt(i); ranges[numUids] = new UidRange(uid, uid); numUids++; @@ -2052,13 +2052,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallType(int chain) { switch (chain) { case FIREWALL_CHAIN_STANDBY: - return FIREWALL_TYPE_BLACKLIST; + return FIREWALL_BLACKLIST; case FIREWALL_CHAIN_DOZABLE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; case FIREWALL_CHAIN_POWERSAVE: - return FIREWALL_TYPE_WHITELIST; + return FIREWALL_WHITELIST; default: - return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST; + return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST; } } @@ -2160,14 +2160,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub private @NonNull String getFirewallRuleName(int chain, int rule) { String ruleName; - if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) { - if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) { + if (getFirewallType(chain) == FIREWALL_WHITELIST) { + if (rule == FIREWALL_RULE_ALLOW) { ruleName = "allow"; } else { ruleName = "deny"; } } else { // Blacklist mode - if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) { + if (rule == FIREWALL_RULE_DENY) { ruleName = "deny"; } else { ruleName = "allow"; @@ -2194,7 +2194,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub private int getFirewallRuleType(int chain, int rule) { if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) { - return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST + return getFirewallType(chain) == FIREWALL_WHITELIST ? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW; } return rule; diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index 62da3f8e01ba..6e5d31640e4f 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -37,6 +37,7 @@ import android.util.SparseArray; import android.util.StatsLog; import com.android.internal.util.ArrayUtils; +import com.android.server.utils.FlagNamespaceUtils; import java.io.File; @@ -194,6 +195,8 @@ public class RescueParty { RecoverySystem.rebootPromptAndWipeUserData(context, TAG); break; } + FlagNamespaceUtils.addToKnownResetNamespaces( + FlagNamespaceUtils.NAMESPACE_NO_PACKAGE); } private static void resetAllSettings(Context context, int mode) throws Exception { @@ -203,14 +206,19 @@ public class RescueParty { final ContentResolver resolver = context.getContentResolver(); try { Settings.Global.resetToDefaultsAsUser(resolver, null, mode, UserHandle.USER_SYSTEM); - } catch (Throwable t) { - res = new RuntimeException("Failed to reset global settings", t); + } catch (Exception e) { + res = new RuntimeException("Failed to reset global settings", e); + } + try { + FlagNamespaceUtils.resetDeviceConfig(mode); + } catch (Exception e) { + res = new RuntimeException("Failed to reset config settings", e); } for (int userId : getAllUserIds()) { try { Settings.Secure.resetToDefaultsAsUser(resolver, null, mode, userId); - } catch (Throwable t) { - res = new RuntimeException("Failed to reset secure settings for " + userId, t); + } catch (Exception e) { + res = new RuntimeException("Failed to reset secure settings for " + userId, e); } } if (res != null) { diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 2f1510e32311..fd946cdfa48a 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -236,7 +237,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PhoneCapability mPhoneCapability = null; - private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @TelephonyManager.RadioPowerState private int mRadioPowerState = TelephonyManager.RADIO_POWER_UNAVAILABLE; @@ -246,14 +247,18 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PreciseDataConnectionState mPreciseDataConnectionState = new PreciseDataConnectionState(); - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = + // Nothing here yet, but putting it here in case we want to add more in the future. + static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0; + + static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO; static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR - | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; + | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST + | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | @@ -637,8 +642,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) { try { if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState rawSs = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false)); + } else { + r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true)); + } } catch (RemoteException ex) { remove(r.binder); } @@ -673,7 +684,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged( new Bundle(mCellLocation[phoneId])); } @@ -722,7 +733,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -809,9 +820,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) { + if ((events & PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE) != 0) { try { - r.callback.onPreferredDataSubIdChanged(mPreferredDataSubId); + r.callback.onActiveDataSubIdChanged(mActiveDataSubId); } catch (RemoteException ex) { remove(r.binder); } @@ -1009,13 +1020,22 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) && idMatch(r.subId, subId, phoneId)) { + try { + ServiceState stateToSend; + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = new ServiceState(state); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + stateToSend = state.sanitizeLocationInfo(false); + } else { + stateToSend = state.sanitizeLocationInfo(true); + } if (DBG) { log("notifyServiceStateForSubscriber: callback.onSSC r=" + r + " subId=" + subId + " phoneId=" + phoneId + " state=" + state); } - r.callback.onServiceStateChanged(new ServiceState(state)); + r.callback.onServiceStateChanged(stateToSend); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1198,7 +1218,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r); @@ -1500,7 +1520,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkLocationAccess(r)) { + checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -1738,23 +1758,23 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - public void notifyPreferredDataSubIdChanged(int preferredSubId) { - if (!checkNotifyPermission("notifyPreferredDataSubIdChanged()")) { + public void notifyActiveDataSubIdChanged(int activeDataSubId) { + if (!checkNotifyPermission("notifyActiveDataSubIdChanged()")) { return; } if (VDBG) { - log("notifyPreferredDataSubIdChanged: preferredSubId=" + preferredSubId); + log("notifyActiveDataSubIdChanged: activeDataSubId=" + activeDataSubId); } synchronized (mRecords) { - mPreferredDataSubId = preferredSubId; + mActiveDataSubId = activeDataSubId; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE)) { + PhoneStateListener.LISTEN_ACTIVE_DATA_SUBID_CHANGE)) { try { - r.callback.onPreferredDataSubIdChanged(preferredSubId); + r.callback.onActiveDataSubIdChanged(activeDataSubId); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -1890,7 +1910,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mBackgroundCallState=" + mBackgroundCallState); pw.println("mSrvccState=" + mSrvccState); pw.println("mPhoneCapability=" + mPhoneCapability); - pw.println("mPreferredDataSubId=" + mPreferredDataSubId); + pw.println("mActiveDataSubId=" + mActiveDataSubId); pw.println("mRadioPowerState=" + mRadioPowerState); pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.println("mCallQuality=" + mCallQuality); @@ -2108,12 +2128,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean checkListenerPermission( int events, int subId, String callingPackage, String message) { + LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(callingPackage) + .setMethod(message + " events: " + events) + .setCallingPid(Binder.getCallingPid()) + .setCallingUid(Binder.getCallingUid()); + + boolean shouldCheckLocationPermissions = false; if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_COARSE_LOCATION, null); - if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(), - callingPackage) != AppOpsManager.MODE_ALLOWED) { - return false; + locationQueryBuilder.setMinSdkVersionForCoarse(0); + shouldCheckLocationPermissions = true; + } + + if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + // Everything that requires fine location started in Q. So far... + locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); + shouldCheckLocationPermissions = true; + } + + if (shouldCheckLocationPermissions) { + LocationAccessPolicy.LocationPermissionResult result = + LocationAccessPolicy.checkLocationPermission( + mContext, locationQueryBuilder.build()); + switch (result) { + case DENIED_HARD: + throw new SecurityException("Unable to listen for events " + events + " due to " + + "insufficient location permissions."); + case DENIED_SOFT: + return false; } } @@ -2139,14 +2182,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) { - // It can have either READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE. - TelephonyPermissions.checkReadPhoneState(mContext, - SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(), - Binder.getCallingUid(), callingPackage, "listen to " - + "LISTEN_PREFERRED_DATA_SUBID_CHANGE"); - } - if ((events & PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); @@ -2228,15 +2263,38 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } - private boolean checkLocationAccess(Record r) { - long token = Binder.clearCallingIdentity(); - try { - return LocationAccessPolicy.canAccessCellLocation(mContext, - r.callingPackage, r.callerUid, r.callerPid, - /*throwOnDeniedPermission*/ false); - } finally { - Binder.restoreCallingIdentity(token); - } + private boolean checkFineLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForFine(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); + } + + private boolean checkCoarseLocationAccess(Record r, int minSdk) { + LocationAccessPolicy.LocationPermissionQuery query = + new LocationAccessPolicy.LocationPermissionQuery.Builder() + .setCallingPackage(r.callingPackage) + .setCallingPid(r.callerPid) + .setCallingUid(r.callerUid) + .setMethod("TelephonyRegistry push") + .setMinSdkVersionForCoarse(minSdk) + .build(); + + return Binder.withCleanCallingIdentity(() -> { + LocationAccessPolicy.LocationPermissionResult locationResult = + LocationAccessPolicy.checkLocationPermission(mContext, query); + return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED; + }); } private void checkPossibleMissNotify(Record r, int phoneId) { @@ -2286,7 +2344,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2336,7 +2394,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = " + mCellLocation[phoneId]); - if (checkLocationAccess(r)) { + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId])); } } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 6b57fcd31450..710a0ba34d4f 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -30,11 +30,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.os.BatteryManager; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.ServiceType; @@ -354,8 +356,12 @@ final class UiModeManagerService extends SystemService { try { synchronized (mLock) { if (mNightMode != mode) { - Settings.Secure.putIntForUser(getContext().getContentResolver(), - Settings.Secure.UI_NIGHT_MODE, mode, user); + // Only persist setting if not transient night mode or not in car mode + if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) { + Settings.Secure.putIntForUser(getContext().getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, mode, user); + } + mNightMode = mode; updateLocked(0, 0); } @@ -438,9 +444,39 @@ final class UiModeManagerService extends SystemService { } } + // Night mode settings in car mode are only persisted below Q. + // When targeting Q, changes are not saved and night mode will be re-read + // from settings when exiting car mode. + private boolean shouldTransientNightWhenInCarMode() { + int uid = Binder.getCallingUid(); + PackageManager packageManager = getContext().getPackageManager(); + String[] packagesForUid = packageManager.getPackagesForUid(uid); + if (packagesForUid == null || packagesForUid.length == 0) { + return false; + } + + try { + ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser( + packagesForUid[0], 0, uid); + + return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q; + } catch (PackageManager.NameNotFoundException ignored) { + } + + return false; + } + void setCarModeLocked(boolean enabled, int flags) { if (mCarModeEnabled != enabled) { mCarModeEnabled = enabled; + + // When transient night mode and exiting car mode, restore night mode from settings + if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) { + Context context = getContext(); + updateNightModeFromSettings(context, + context.getResources(), + UserHandle.getCallingUserId()); + } } mCarModeEnableFlags = flags; } @@ -498,7 +534,9 @@ final class UiModeManagerService extends SystemService { uiMode |= mNightMode << 4; } - if (mPowerSave) { + // Override night mode in power save mode if not transient night mode or not in car mode + boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode(); + if (mPowerSave && shouldOverrideNight) { uiMode &= ~Configuration.UI_MODE_NIGHT_NO; uiMode |= Configuration.UI_MODE_NIGHT_YES; } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 026430b5469d..aebe2b78a853 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -319,10 +319,6 @@ public final class ActiveServices { ServiceRecord r = mDelayedStartList.remove(0); if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (exec next): " + r); - if (r.pendingStarts.size() <= 0) { - Slog.w(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested - + " delayedStop=" + r.delayedStop); - } if (DEBUG_DELAYED_SERVICE) { if (mDelayedStartList.size() > 0) { Slog.v(TAG_SERVICE, "Remaining delayed list:"); @@ -332,11 +328,16 @@ public final class ActiveServices { } } r.delayed = false; - try { - startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true, - false); - } catch (TransactionTooLargeException e) { - // Ignore, nobody upstack cares. + if (r.pendingStarts.size() <= 0) { + Slog.wtf(TAG, "**** NO PENDING STARTS! " + r + " startReq=" + r.startRequested + + " delayedStop=" + r.delayedStop); + } else { + try { + startServiceInnerLocked(this, r.pendingStarts.get(0).intent, r, false, true, + false); + } catch (TransactionTooLargeException e) { + // Ignore, nobody upstack cares. + } } } if (mStartingBackground.size() > 0) { @@ -2978,6 +2979,7 @@ public final class ActiveServices { // Clear start entries. r.clearDeliveredStartsLocked(); r.pendingStarts.clear(); + smap.mDelayedStartList.remove(r); if (r.app != null) { synchronized (r.stats.getBatteryStats()) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c4a9db6b7262..98f2c9f21f3a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2704,8 +2704,8 @@ public class ActivityManagerService extends IActivityManager.Stub public void batterySendBroadcast(Intent intent) { synchronized (this) { broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, - -1, SYSTEM_UID, UserHandle.USER_ALL); + OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -3823,12 +3823,13 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId); if (isInstantApp) { intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName); - broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0, - null, null, permission.ACCESS_INSTANT_APPS, null, false, false, - resolvedUserId, false); + broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null, + null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false, + false, resolvedUserId, false); } else { - broadcastIntentInPackage("android", SYSTEM_UID, intent, null, null, 0, - null, null, null, null, false, false, resolvedUserId, false); + broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null, + null, 0, null, null, null, null, false, false, resolvedUserId, + false); } if (observer != null) { @@ -4263,7 +4264,8 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid)); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, UserHandle.getUserId(uid)); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.getUserId(uid)); } private void cleanupDisabledPackageComponentsLocked( @@ -8709,6 +8711,8 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal.showSystemReadyErrorDialogsIfNeeded(); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); long ident = Binder.clearCallingIdentity(); try { Intent intent = new Intent(Intent.ACTION_USER_STARTED); @@ -8717,7 +8721,7 @@ public class ActivityManagerService extends IActivityManager.Stub intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, currentUserId); intent = new Intent(Intent.ACTION_USER_STARTING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -8731,7 +8735,8 @@ public class ActivityManagerService extends IActivityManager.Stub } }, 0, null, null, new String[] {INTERACT_ACROSS_USERS}, OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + UserHandle.USER_ALL); } catch (Throwable t) { Slog.wtf(TAG, "Failed sending first user broadcasts", t); } finally { @@ -14369,10 +14374,12 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered, - sticky, callingPid, callingUid, userId, false /* allowBackgroundActivityStarts */); + sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId, + false /* allowBackgroundActivityStarts */); } @GuardedBy("this") @@ -14380,8 +14387,8 @@ public class ActivityManagerService extends IActivityManager.Stub String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId, - boolean allowBackgroundActivityStarts) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId, boolean allowBackgroundActivityStarts) { intent = new Intent(intent); final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid); @@ -14430,7 +14437,7 @@ public class ActivityManagerService extends IActivityManager.Stub // PendingIntent), because that who is actually supplied the arguments. if (checkComponentPermission( android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, - Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + realCallingPid, realCallingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: " + intent.getAction() + " broadcast from " + callerPackage + " (pid=" + callingPid @@ -14448,6 +14455,25 @@ public class ActivityManagerService extends IActivityManager.Stub + " has background restrictions"); return ActivityManager.START_CANCELED; } + if (brOptions.allowsBackgroundActivityStarts()) { + // See if the caller is allowed to do this. Note we are checking against + // the actual real caller (not whoever provided the operation as say a + // PendingIntent), because that who is actually supplied the arguments. + if (checkComponentPermission( + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED) { + String msg = "Permission Denial: " + intent.getAction() + + " broadcast from " + callerPackage + " (pid=" + callingPid + + ", uid=" + callingUid + ")" + + " requires " + + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } else { + allowBackgroundActivityStarts = true; + } + } } // Verify that protected broadcasts are only being sent by system code, @@ -15118,15 +15144,15 @@ public class ActivityManagerService extends IActivityManager.Stub callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky, - callingPid, callingUid, userId); + callingPid, callingUid, callingUid, callingPid, userId); Binder.restoreCallingIdentity(origId); return res; } } - int broadcastIntentInPackage(String packageName, int uid, - Intent intent, String resolvedType, IIntentReceiver resultTo, + int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, + int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky, int userId, boolean allowBackgroundActivityStarts) { @@ -15139,7 +15165,8 @@ public class ActivityManagerService extends IActivityManager.Stub int res = broadcastIntentLocked(null, packageName, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, OP_NONE, bOptions, serialized, - sticky, -1, uid, userId, allowBackgroundActivityStarts); + sticky, -1, uid, realCallingUid, realCallingPid, userId, + allowBackgroundActivityStarts); Binder.restoreCallingIdentity(origId); return res; } @@ -17721,15 +17748,16 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public int broadcastIntentInPackage(String packageName, int uid, Intent intent, - String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, - Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized, - boolean sticky, int userId, boolean allowBackgroundActivityStarts) { + public int broadcastIntentInPackage(String packageName, int uid, int realCallingUid, + int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, + int resultCode, String resultData, Bundle resultExtras, String requiredPermission, + Bundle bOptions, boolean serialized, boolean sticky, int userId, + boolean allowBackgroundActivityStarts) { synchronized (ActivityManagerService.this) { return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid, - intent, resolvedType, resultTo, resultCode, resultData, resultExtras, - requiredPermission, bOptions, serialized, sticky, userId, - allowBackgroundActivityStarts); + realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode, + resultData, resultExtras, requiredPermission, bOptions, serialized, sticky, + userId, allowBackgroundActivityStarts); } } @@ -17830,8 +17858,8 @@ public class ActivityManagerService extends IActivityManager.Stub | Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) { intent = new Intent(Intent.ACTION_LOCALE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND @@ -17841,8 +17869,8 @@ public class ActivityManagerService extends IActivityManager.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } // Send a broadcast to PackageInstallers if the configuration change is interesting @@ -17857,7 +17885,7 @@ public class ActivityManagerService extends IActivityManager.Stub new String[] { android.Manifest.permission.INSTALL_PACKAGES }; broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL); } } } @@ -17881,7 +17909,8 @@ public class ActivityManagerService extends IActivityManager.Stub } broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null, - OP_NONE, null, false, false, -1, SYSTEM_UID, UserHandle.USER_ALL); + OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } } @@ -18100,8 +18129,10 @@ public class ActivityManagerService extends IActivityManager.Stub if (!queue.isIdle()) { final String msg = "Waiting for queue " + queue + " to become idle..."; pw.println(msg); + pw.println(queue.describeState()); pw.flush(); Slog.v(TAG, msg); + queue.cancelDeferrals(); idle = false; } } diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index c7e4fc78c013..17ffd9c5e093 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -28,6 +28,7 @@ import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION; import android.app.ActivityManager; import android.app.ActivityThread; +import android.os.Debug; import android.os.Handler; import android.os.Message; import android.os.Process; @@ -410,6 +411,7 @@ public final class AppCompactor { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact " + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full") + ": " + name); + long zramFreeKbBefore = Debug.getZramFreeKb(); long[] rssBefore = Process.getRss(pid); FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim"); fos.write(action.getBytes()); @@ -417,10 +419,12 @@ public final class AppCompactor { long[] rssAfter = Process.getRss(pid); long end = SystemClock.uptimeMillis(); long time = end - start; + long zramFreeKbAfter = Debug.getZramFreeKb(); EventLog.writeEvent(EventLogTags.AM_COMPACT, pid, name, action, rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, - lastCompactAction, lastCompactTime, msg.arg1, msg.arg2); + lastCompactAction, lastCompactTime, msg.arg1, msg.arg2, + zramFreeKbBefore, zramFreeKbAfter); // Note that as above not taking mPhenoTypeFlagLock here to avoid locking // on every single compaction for a flag that will seldom change and the // impact of reading the wrong value here is low. @@ -429,7 +433,8 @@ public final class AppCompactor { rssBefore[0], rssBefore[1], rssBefore[2], rssBefore[3], rssAfter[0], rssAfter[1], rssAfter[2], rssAfter[3], time, lastCompactAction, lastCompactTime, msg.arg1, - ActivityManager.processStateAmToProto(msg.arg2)); + ActivityManager.processStateAmToProto(msg.arg2), + zramFreeKbBefore, zramFreeKbAfter); } synchronized (mAm) { proc.lastCompactTime = end; diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 24543b7974df..236797b57556 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -477,6 +477,10 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { mStats.updateRpmStatsLocked(); } + if ((updateFlags & UPDATE_RAIL) != 0) { + mStats.updateRailStatsLocked(); + } + if (bluetoothInfo != null) { if (bluetoothInfo.isValid()) { mStats.updateBluetoothStateLocked(bluetoothInfo); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 08900328a200..4d5cb8cb4473 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -59,6 +59,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.PowerProfile; +import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; import com.android.internal.util.DumpUtils; import com.android.internal.util.ParseUtils; @@ -84,7 +85,8 @@ import java.util.concurrent.Future; */ public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener, - BatteryStatsImpl.PlatformIdleStateCallback { + BatteryStatsImpl.PlatformIdleStateCallback, + BatteryStatsImpl.RailEnergyDataCallback { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; @@ -98,6 +100,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub private native void getLowPowerStats(RpmStats rpmStats); private native int getPlatformLowPowerStats(ByteBuffer outBuffer); private native int getSubsystemLowPowerStats(ByteBuffer outBuffer); + private native void getRailEnergyPowerStats(RailStats railStats); private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8 .newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) @@ -121,6 +124,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub } @Override + public void fillRailDataStats(RailStats railStats) { + if (DBG) Slog.d(TAG, "begin getRailEnergyPowerStats"); + try { + getRailEnergyPowerStats(railStats); + } finally { + if (DBG) Slog.d(TAG, "end getRailEnergyPowerStats"); + } + } + + @Override public String getPlatformLowPowerStats() { if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats"); try { @@ -177,7 +190,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub return (umi != null) ? umi.getUserIds() : null; } }; - mStats = new BatteryStatsImpl(systemDir, handler, this, mUserManagerUserInfoProvider); + mStats = new BatteryStatsImpl(systemDir, handler, this, + this, mUserManagerUserInfoProvider); mWorker = new BatteryExternalStatsWorker(context, mStats); mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( @@ -1460,7 +1474,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpProtoLocked( @@ -1498,7 +1513,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); BatteryStatsImpl checkinStats = new BatteryStatsImpl( - null, mStats.mHandler, null, mUserManagerUserInfoProvider); + null, mStats.mHandler, null, null, + mUserManagerUserInfoProvider); checkinStats.readSummaryFromParcel(in); in.recycle(); checkinStats.dumpCheckinLocked(mContext, pw, apps, flags, diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java index 6371cd376d19..0b38ef94de1e 100644 --- a/services/core/java/com/android/server/am/BroadcastDispatcher.java +++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java @@ -65,6 +65,14 @@ public class BroadcastDispatcher { broadcasts.add(br); } + int size() { + return broadcasts.size(); + } + + boolean isEmpty() { + return broadcasts.isEmpty(); + } + void writeToProto(ProtoOutputStream proto, long fieldId) { for (BroadcastRecord br : broadcasts) { br.writeToProto(proto, fieldId); @@ -252,22 +260,48 @@ public class BroadcastDispatcher { synchronized (mLock) { return mCurrentBroadcast == null && mOrderedBroadcasts.isEmpty() - && mDeferredBroadcasts.isEmpty() - && mAlarmBroadcasts.isEmpty(); + && isDeferralsListEmpty(mDeferredBroadcasts) + && isDeferralsListEmpty(mAlarmBroadcasts); + } + } + + private static int pendingInDeferralsList(ArrayList<Deferrals> list) { + int pending = 0; + final int numEntries = list.size(); + for (int i = 0; i < numEntries; i++) { + pending += list.get(i).size(); } + return pending; + } + + private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) { + return pendingInDeferralsList(list) == 0; } /** - * Not quite the traditional size() measurement; includes any in-process but - * not yet retired active outbound broadcast. + * Strictly for logging, describe the currently pending contents in a human- + * readable way */ - public int totalUndelivered() { - synchronized (mLock) { - return mAlarmBroadcasts.size() - + mDeferredBroadcasts.size() - + mOrderedBroadcasts.size() - + (mCurrentBroadcast == null ? 0 : 1); - } + public String describeStateLocked() { + final StringBuilder sb = new StringBuilder(128); + if (mCurrentBroadcast != null) { + sb.append("1 in flight, "); + } + sb.append(mOrderedBroadcasts.size()); + sb.append(" ordered"); + int n = pendingInDeferralsList(mAlarmBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferrals in alarm recipients"); + } + n = pendingInDeferralsList(mDeferredBroadcasts); + if (n > 0) { + sb.append(", "); + sb.append(n); + sb.append(" deferred"); + } + return sb.toString(); } // ---------------------------------- @@ -579,6 +613,26 @@ public class BroadcastDispatcher { } } + /** + * Cancel all current deferrals; that is, make all currently-deferred broadcasts + * immediately deliverable. Used by the wait-for-broadcast-idle mechanism. + */ + public void cancelDeferrals() { + synchronized (mLock) { + zeroDeferralTimes(mAlarmBroadcasts); + zeroDeferralTimes(mDeferredBroadcasts); + } + } + + private static void zeroDeferralTimes(ArrayList<Deferrals> list) { + final int num = list.size(); + for (int i = 0; i < num; i++) { + Deferrals d = list.get(i); + // Safe to do this in-place because it won't break ordering + d.deferUntil = d.deferredBy = 0; + } + } + // ---------------------------------- /** diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index f0b137a6ccb1..d9ea1da79f56 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -898,6 +898,11 @@ public final class BroadcastQueue { for (int i = perms.length-1; i >= 0; i--) { try { PermissionInfo pi = pm.getPermissionInfo(perms[i], "android", 0); + if (pi == null) { + // a required permission that no package has actually + // defined cannot be signature-required. + return false; + } if ((pi.protectionLevel & (PermissionInfo.PROTECTION_MASK_BASE | PermissionInfo.PROTECTION_FLAG_PRIVILEGED)) != PermissionInfo.PROTECTION_SIGNATURE) { @@ -923,8 +928,8 @@ public final class BroadcastQueue { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast [" + mQueueName + "]: " - + mParallelBroadcasts.size() + " parallel broadcasts, " - + mDispatcher.totalUndelivered() + " ordered broadcasts"); + + mParallelBroadcasts.size() + " parallel broadcasts; " + + mDispatcher.describeStateLocked()); mService.updateCpuStats(); @@ -1822,11 +1827,24 @@ public final class BroadcastQueue { record.intent == null ? "" : record.intent.getAction()); } - final boolean isIdle() { + boolean isIdle() { return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty() && (mPendingBroadcast == null); } + // Used by wait-for-broadcast-idle : fast-forward all current deferrals to + // be immediately deliverable. + void cancelDeferrals() { + mDispatcher.cancelDeferrals(); + } + + String describeState() { + synchronized (mService) { + return mParallelBroadcasts.size() + " parallel; " + + mDispatcher.describeStateLocked(); + } + } + void writeToProto(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 8ffb67a1405c..06d015234011 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -79,6 +79,7 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLISTS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index a71f6af80bec..4b12e43692a6 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -138,4 +138,4 @@ option java_package com.android.server.am 30061 am_remove_task (Task ID|1|5), (Stack ID|1|5) # The task is being compacted -30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2) +30063 am_compact (Pid|1|5),(Process Name|3),(Action|3),(BeforeRssTotal|2|2),(BeforeRssFile|2|2),(BeforeRssAnon|2|2),(BeforeRssSwap|2|2),(AfterRssTotal|2|2),(AfterRssFile|2|2),(AfterRssAnon|2|2),(AfterRssSwap|2|2),(Time|2|3),(LastAction|1|2),(LastActionTimestamp|2|3),(setAdj|1|2),(procState|1|2),(BeforeZRAMFree|2|2),(AfterZRAMFree|2|2) diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 93a71e5a2ed4..4e03b72e6ce6 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1692,7 +1692,8 @@ public final class OomAdjuster { (app.curAdj == ProcessList.PREVIOUS_APP_ADJ || app.curAdj == ProcessList.HOME_APP_ADJ)) { mAppCompact.compactAppSome(app); - } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ + } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ + || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ) && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) { mAppCompact.compactAppFull(app); diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index af56352b8cc0..a08c829f0576 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -423,9 +423,9 @@ public final class PendingIntentRecord extends IIntentSender.Stub { // If a completion callback has been requested, require // that the broadcast be delivered synchronously int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName, - uid, finalIntent, resolvedType, finishedReceiver, code, null, null, - requiredPermission, options, (finishedReceiver != null), - false, userId, + uid, callingUid, callingPid, finalIntent, resolvedType, + finishedReceiver, code, null, null, requiredPermission, options, + (finishedReceiver != null), false, userId, mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken) || allowTrampoline); if (sent == ActivityManager.BROADCAST_SUCCESS) { diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 3ea114758498..376999dfd80d 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ResolveInfo; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -108,7 +109,7 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { mIntent.setComponent(componentName); mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID, - Process.SYSTEM_UID, mUserId); + Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId); } @Override diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 0d49e4cdc9d0..3a61dd987cf5 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1498,6 +1498,13 @@ public final class ProcessList { // Also turn on CheckJNI for debuggable apps. It's quite // awkward to turn on otherwise. runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; + + // Check if the developer does not want ART verification + if (android.provider.Settings.Global.getInt(mService.mContext.getContentResolver(), + android.provider.Settings.Global.ART_VERIFIER_VERIFY_DEBUGGABLE, 1) == 0) { + runtimeFlags |= Zygote.DISABLE_VERIFIER; + Slog.w(TAG_PROCESSES, app + ": ART verification disabled"); + } } // Run the app in safe mode if its manifest requests so or the // system is booted in safe mode. @@ -1705,9 +1712,15 @@ public final class ProcessList { zygoteProcesses.remove(app); if (zygoteProcesses.size() == 0) { mService.mHandler.removeMessages(KILL_APP_ZYGOTE_MSG); - Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG); - msg.obj = appZygote; - mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS); + if (app.removed) { + // If we stopped this process because the package hosting it was removed, + // there's no point in delaying the app zygote kill. + killAppZygoteIfNeededLocked(appZygote); + } else { + Message msg = mService.mHandler.obtainMessage(KILL_APP_ZYGOTE_MSG); + msg.obj = appZygote; + mService.mHandler.sendMessageDelayed(msg, KILL_APP_ZYGOTE_DELAY_MS); + } } } } @@ -2162,6 +2175,29 @@ public final class ProcessList { for (int i=0; i<N; i++) { removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason); } + // See if there are any app zygotes running for this packageName / UID combination, + // and kill it if so. + final ArrayList<AppZygote> zygotesToKill = new ArrayList<>(); + for (SparseArray<AppZygote> appZygotes : mAppZygotes.getMap().values()) { + for (int i = 0; i < appZygotes.size(); ++i) { + final int appZygoteUid = appZygotes.keyAt(i); + if (userId != UserHandle.USER_ALL && UserHandle.getUserId(appZygoteUid) != userId) { + continue; + } + if (appId >= 0 && UserHandle.getAppId(appZygoteUid) != appId) { + continue; + } + final AppZygote appZygote = appZygotes.valueAt(i); + if (packageName != null + && !packageName.equals(appZygote.getAppInfo().packageName)) { + continue; + } + zygotesToKill.add(appZygote); + } + } + for (AppZygote appZygote : zygotesToKill) { + killAppZygoteIfNeededLocked(appZygote); + } mService.updateOomAdjLocked(); return N > 0; } diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index 7f6648a3d174..ac20f6c7eaaf 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -371,7 +371,8 @@ class UserController implements Handler.Callback { | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mInjector.broadcastIntent(intent, null, resultTo, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), userId); } // We need to delay unlocking managed profiles until the parent user @@ -471,7 +472,7 @@ class UserController implements Handler.Callback { Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); mInjector.broadcastIntent(unlockedIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - userId); + Binder.getCallingUid(), Binder.getCallingPid(), userId); if (getUserInfo(userId).isManagedProfile()) { UserInfo parent = mInjector.getUserManager().getProfileParent(userId); @@ -484,8 +485,8 @@ class UserController implements Handler.Callback { | Intent.FLAG_RECEIVER_FOREGROUND); mInjector.broadcastIntent(profileUnlockedIntent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, - parent.id); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), parent.id); } } @@ -543,7 +544,8 @@ class UserController implements Handler.Callback { mInjector.getUserManager().makeInitialized(userInfo.id); } }, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), userId); } } @@ -573,7 +575,8 @@ class UserController implements Handler.Callback { } }, 0, null, null, new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED}, - AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId); + AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, + Binder.getCallingUid(), Binder.getCallingPid(), userId); } int restartUser(final int userId, final boolean foreground) { @@ -696,7 +699,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(stoppingIntent, null, stoppingReceiver, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); }); } } @@ -735,7 +739,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(shutdownIntent, null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, userId); + null, true, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), userId); } void finishUserStopped(UserState uss) { @@ -834,7 +839,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), + Binder.getCallingPid(), UserHandle.USER_ALL); } /** @@ -950,6 +956,8 @@ class UserController implements Handler.Callback { Slog.i(TAG, "Starting userid:" + userId + " fg:" + foreground); + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); final long ident = Binder.clearCallingIdentity(); try { final int oldUserId = getCurrentUserId(); @@ -1088,7 +1096,7 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, userId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, userId); } if (foreground) { @@ -1111,7 +1119,8 @@ class UserController implements Handler.Callback { } }, 0, null, null, new String[]{INTERACT_ACROSS_USERS}, AppOpsManager.OP_NONE, - null, true, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL); + null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -1427,6 +1436,8 @@ class UserController implements Handler.Callback { } void sendUserSwitchBroadcasts(int oldUserId, int newUserId) { + final int callingUid = Binder.getCallingUid(); + final int callingPid = Binder.getCallingPid(); long ident = Binder.clearCallingIdentity(); try { Intent intent; @@ -1442,7 +1453,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, profileUserId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + profileUserId); } } if (newUserId >= 0) { @@ -1457,7 +1469,8 @@ class UserController implements Handler.Callback { intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, SYSTEM_UID, profileUserId); + null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, + profileUserId); } intent = new Intent(Intent.ACTION_USER_SWITCHED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY @@ -1466,8 +1479,8 @@ class UserController implements Handler.Callback { mInjector.broadcastIntent(intent, null, null, 0, null, null, new String[] {android.Manifest.permission.MANAGE_USERS}, - AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, - UserHandle.USER_ALL); + AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, + callingPid, UserHandle.USER_ALL); } } finally { Binder.restoreCallingIdentity(ident); @@ -2107,12 +2120,14 @@ class UserController implements Handler.Callback { protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, - boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) { + boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { // TODO b/64165549 Verify that mLock is not held before calling AMS methods synchronized (mService) { return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, - ordered, sticky, callingPid, callingUid, userId); + ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, + userId); } } diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 0b6a4329d15b..bbe4ed15b3a0 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -177,13 +177,12 @@ public class AppBindingService extends Binder { * Handle boot phase PHASE_ACTIVITY_MANAGER_READY. */ private void onPhaseActivityManagerReady() { + // RoleManager doesn't tell us about upgrade, so we still need to listen for app upgrades. + // (app uninstall/disable will be notified by RoleManager.) final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addDataScheme("package"); - packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL, packageFilter, null, mHandler); @@ -256,14 +255,6 @@ public class AppBindingService extends Binder { handlePackageAddedReplacing(packageName, userId); } break; - case Intent.ACTION_PACKAGE_REMOVED: - if (!replacing) { - handlePackageRemoved(packageName, userId); - } - break; - case Intent.ACTION_PACKAGE_CHANGED: - handlePackageChanged(packageName, userId); - break; } } }; @@ -371,31 +362,6 @@ public class AppBindingService extends Binder { } } - private void handlePackageRemoved(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package uninstall"); - } - } - } - - private void handlePackageChanged(String packageName, int userId) { - if (DEBUG) { - Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName); - } - synchronized (mLock) { - final AppServiceFinder finder = findFinderLocked(userId, packageName); - if (finder != null) { - unbindServicesLocked(userId, finder, "package changed"); - bindServicesLocked(userId, finder, "package changed"); - } - } - } - private void rebindAllLocked(String reason) { for (int i = 0; i < mRunningUsers.size(); i++) { if (!mRunningUsers.valueAt(i)) { diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java index 4c5f1a1c7b49..3663518bf7b9 100644 --- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java +++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java @@ -16,14 +16,10 @@ package com.android.server.appbinding.finders; -import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL; - import android.Manifest.permission; -import android.content.BroadcastReceiver; -import android.content.ComponentName; +import android.app.role.OnRoleHoldersChangedListener; +import android.app.role.RoleManager; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; @@ -35,7 +31,8 @@ import android.text.TextUtils; import android.util.Slog; import com.android.internal.R; -import com.android.internal.telephony.SmsApplication; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.server.appbinding.AppBindingConstants; import java.util.function.BiConsumer; @@ -45,10 +42,15 @@ import java.util.function.BiConsumer; */ public class CarrierMessagingClientServiceFinder extends AppServiceFinder<CarrierMessagingClientService, ICarrierMessagingClientService> { + + private final RoleManager mRoleManager; + public CarrierMessagingClientServiceFinder(Context context, BiConsumer<AppServiceFinder, Integer> listener, Handler callbackHandler) { super(context, listener, callbackHandler); + + mRoleManager = context.getSystemService(RoleManager.class); } @Override @@ -84,9 +86,8 @@ public class CarrierMessagingClientServiceFinder @Override public String getTargetPackage(int userId) { - final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser( - mContext, /* updateIfNeeded= */ true, userId); - String ret = cn == null ? null : cn.getPackageName(); + final String ret = CollectionUtils.firstOrNull(mRoleManager.getRoleHoldersAsUser( + RoleManager.ROLE_SMS, UserHandle.of(userId))); if (DEBUG) { Slog.d(TAG, "getTargetPackage()=" + ret); @@ -97,9 +98,8 @@ public class CarrierMessagingClientServiceFinder @Override public void startMonitoring() { - final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); - mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter, - /* permission= */ null, mHandler); + mRoleManager.addOnRoleHoldersChangedListenerAsUser( + BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL); } @Override @@ -118,12 +118,9 @@ public class CarrierMessagingClientServiceFinder return constants.SMS_APP_BIND_FLAGS; } - private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) { - mListener.accept(CarrierMessagingClientServiceFinder.this, getSendingUserId()); - } + private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> { + if (RoleManager.ROLE_SMS.equals(role)) { + mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier()); } }; } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 0e3309088f21..70c28a854512 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -38,6 +38,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.AppOpsManager.HistoricalOps; +import android.app.AppOpsManager.HistoricalOpsRequest; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.content.BroadcastReceiver; @@ -1024,25 +1025,29 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void getHistoricalOps(int uid, @NonNull String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, @NonNull RemoteCallback callback) { - Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0, - "uid must be " + Process.INVALID_UID + " or non negative"); - Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis, - "beginTimeMillis must be non negative and lesser than endTimeMillis"); + // Use the builder to validate arguments. + final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + beginTimeMillis, endTimeMillis) + .setUid(uid) + .setPackageName(packageName) + .setOpNames(opNames) + .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); - checkValidOpsOrNull(opNames); mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + final String[] opNamesArray = (opNames != null) + ? opNames.toArray(new String[opNames.size()]) : null; if (mHistoricalRegistry.getMode() == AppOpsManager.HISTORICAL_MODE_DISABLED) { // TODO (bug:122218838): Remove once the feature fully enabled. - getHistoricalPackagesOpsCompat(uid, packageName, opNames, beginTimeMillis, + getHistoricalPackagesOpsCompat(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } else { // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOps(uid, packageName, opNames, + mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } } @@ -1101,20 +1106,25 @@ public class AppOpsService extends IAppOpsService.Stub { @Override public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, - @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, + @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis, @NonNull RemoteCallback callback) { - Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0, - "uid must be " + Process.INVALID_UID + " or non negative"); - Preconditions.checkArgument(beginTimeMillis >= 0 && beginTimeMillis < endTimeMillis, - "beginTimeMillis must be non negative and lesser than endTimeMillis"); + // Use the builder to validate arguments. + final HistoricalOpsRequest request = new HistoricalOpsRequest.Builder( + beginTimeMillis, endTimeMillis) + .setUid(uid) + .setPackageName(packageName) + .setOpNames(opNames) + .build(); Preconditions.checkNotNull(callback, "callback cannot be null"); - checkValidOpsOrNull(opNames); mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + final String[] opNamesArray = (opNames != null) + ? opNames.toArray(new String[opNames.size()]) : null; + // Must not hold the appops lock - mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNames, + mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray, beginTimeMillis, endTimeMillis, callback); } @@ -4266,16 +4276,6 @@ public class AppOpsService extends IAppOpsService.Stub { return packageNames; } - private static void checkValidOpsOrNull(String[] opNames) { - if (opNames != null) { - for (String opName : opNames) { - if (AppOpsManager.strOpToOp(opName) == AppOpsManager.OP_NONE) { - throw new IllegalArgumentException("Unknown op: " + opName); - } - } - } - } - private final class ClientRestrictionState implements DeathRecipient { private final IBinder token; SparseArray<boolean[]> perUserRestrictions; diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java index 9a1d7bf0053d..47c9b8639760 100644 --- a/services/core/java/com/android/server/attention/AttentionManagerService.java +++ b/services/core/java/com/android/server/attention/AttentionManagerService.java @@ -104,8 +104,7 @@ public class AttentionManagerService extends SystemService { @Override public void onSwitchUser(int userId) { - cancelAndUnbindLocked(peekUserStateLocked(userId), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(peekUserStateLocked(userId)); } /** Resolves and sets up the attention service if it had not been done yet. */ @@ -152,7 +151,8 @@ public class AttentionManagerService extends SystemService { } synchronized (mLock) { - unbindAfterTimeoutLocked(); + final long now = SystemClock.uptimeMillis(); + freeIfInactiveLocked(); final UserState userState = getOrCreateCurrentUserStateLocked(); // lazily start the service, which should be very lightweight to start @@ -172,7 +172,7 @@ public class AttentionManagerService extends SystemService { try { // throttle frequent requests final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache; - if (attentionCheckCache != null && SystemClock.uptimeMillis() + if (attentionCheckCache != null && now < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) { callback.onSuccess(requestCode, attentionCheckCache.mResult, attentionCheckCache.mTimestamp); @@ -190,6 +190,7 @@ public class AttentionManagerService extends SystemService { userState.mAttentionCheckCache = new AttentionCheckCache( SystemClock.uptimeMillis(), result, timestamp); + userState.mCurrentAttentionCheckIsFulfilled = true; } StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, result); @@ -198,14 +199,10 @@ public class AttentionManagerService extends SystemService { @Override public void onFailure(int requestCode, int error) { callback.onFailure(requestCode, error); + userState.mCurrentAttentionCheckIsFulfilled = true; StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED, error); } - - @Override - public IBinder asBinder() { - return null; - } }); } catch (RemoteException e) { Slog.e(LOG_TAG, "Cannot call into the AttentionService"); @@ -219,7 +216,10 @@ public class AttentionManagerService extends SystemService { /** Cancels the specified attention check. */ public void cancelAttentionCheck(int requestCode) { synchronized (mLock) { - final UserState userState = getOrCreateCurrentUserStateLocked(); + final UserState userState = peekCurrentUserStateLocked(); + if (userState == null) { + return; + } if (userState.mService == null) { if (userState.mPendingAttentionCheck != null && userState.mPendingAttentionCheck.mRequestCode == requestCode) { @@ -236,8 +236,12 @@ public class AttentionManagerService extends SystemService { } @GuardedBy("mLock") - private void unbindAfterTimeoutLocked() { - mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED, + private void freeIfInactiveLocked() { + // If we are called here, it means someone used the API again - reset the timer then. + mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION); + + // Schedule resources cleanup if no one calls the API again. + mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION, CONNECTION_TTL_MILLIS); } @@ -264,12 +268,14 @@ public class AttentionManagerService extends SystemService { } @GuardedBy("mLock") - UserState peekCurrentUserStateLocked() { + @Nullable + private UserState peekCurrentUserStateLocked() { return peekUserStateLocked(ActivityManager.getCurrentUser()); } @GuardedBy("mLock") - UserState peekUserStateLocked(int userId) { + @Nullable + private UserState peekUserStateLocked(int userId) { return mUserStates.get(userId); } @@ -406,6 +412,8 @@ public class AttentionManagerService extends SystemService { @GuardedBy("mLock") int mCurrentAttentionCheckRequestCode; @GuardedBy("mLock") + boolean mCurrentAttentionCheckIsFulfilled; + @GuardedBy("mLock") PendingAttentionCheck mPendingAttentionCheck; @GuardedBy("mLock") @@ -501,7 +509,7 @@ public class AttentionManagerService extends SystemService { } private class AttentionHandler extends Handler { - private static final int CONNECTION_EXPIRED = 1; + private static final int CHECK_CONNECTION_EXPIRATION = 1; private static final int ATTENTION_CHECK_TIMEOUT = 2; AttentionHandler() { @@ -511,19 +519,26 @@ public class AttentionManagerService extends SystemService { public void handleMessage(Message msg) { switch (msg.what) { // Do not occupy resources when not in use - unbind proactively. - case CONNECTION_EXPIRED: { + case CHECK_CONNECTION_EXPIRATION: { for (int i = 0; i < mUserStates.size(); i++) { - cancelAndUnbindLocked(mUserStates.valueAt(i), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(mUserStates.valueAt(i)); } - } break; // Callee is no longer interested in the attention check result - cancel. case ATTENTION_CHECK_TIMEOUT: { - cancelAndUnbindLocked(peekCurrentUserStateLocked(), - AttentionService.ATTENTION_FAILURE_TIMED_OUT); + synchronized (mLock) { + final UserState userState = peekCurrentUserStateLocked(); + if (userState != null) { + // If not called back already. + if (!userState.mCurrentAttentionCheckIsFulfilled) { + cancel(userState, + AttentionService.ATTENTION_FAILURE_TIMED_OUT); + } + + } + } } break; @@ -533,25 +548,29 @@ public class AttentionManagerService extends SystemService { } } + private void cancel(UserState userState, @AttentionFailureCodes int failureCode) { + if (userState != null && userState.mService != null) { + try { + userState.mService.cancelAttentionCheck( + userState.mCurrentAttentionCheckRequestCode); + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Unable to cancel attention check"); + } + + if (userState.mPendingAttentionCheck != null) { + userState.mPendingAttentionCheck.cancel(failureCode); + } + } + } + @GuardedBy("mLock") - private void cancelAndUnbindLocked(UserState userState, - @AttentionFailureCodes int failureCode) { + private void cancelAndUnbindLocked(UserState userState) { synchronized (mLock) { - if (userState != null && userState.mService != null) { - try { - userState.mService.cancelAttentionCheck( - userState.mCurrentAttentionCheckRequestCode); - } catch (RemoteException e) { - Slog.e(LOG_TAG, "Unable to cancel attention check"); - } + cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN); - if (userState.mPendingAttentionCheck != null) { - userState.mPendingAttentionCheck.cancel(failureCode); - } - mContext.unbindService(userState.mConnection); - userState.mConnection.cleanupService(); - mUserStates.remove(userState.mUserId); - } + mContext.unbindService(userState.mConnection); + userState.mConnection.cleanupService(); + mUserStates.remove(userState.mUserId); } } @@ -563,8 +582,7 @@ public class AttentionManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) { - cancelAndUnbindLocked(peekCurrentUserStateLocked(), - AttentionService.ATTENTION_FAILURE_UNKNOWN); + cancelAndUnbindLocked(peekCurrentUserStateLocked()); } } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 6df60d6bdd3a..9af57daa259b 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -280,9 +280,9 @@ import java.util.ArrayList; AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE, AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource); - // Un-mute ringtone stream volume - mAudioService.setUpdateRingerModeServiceInt(); } + // Un-mute ringtone stream volume + mAudioService.postUpdateRingerModeServiceInt(); } /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index a6643d49c79f..d902201df212 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -248,6 +248,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_NOTIFY_VOL_EVENT = 22; private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23; private static final int MSG_ENABLE_SURROUND_FORMATS = 24; + private static final int MSG_UPDATE_RINGER_MODE = 25; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) @@ -753,6 +754,7 @@ public class AudioService extends IAudioService.Stub intentFilter.addAction(Intent.ACTION_USER_FOREGROUND); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + intentFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false); @@ -2720,7 +2722,11 @@ public class AudioService extends IAudioService.Stub } } - /*package*/ void setUpdateRingerModeServiceInt() { + /*package*/ void postUpdateRingerModeServiceInt() { + sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0); + } + + private void onUpdateRingerModeServiceInt() { setRingerModeInt(getRingerModeInternal(), false); } @@ -3217,6 +3223,21 @@ public class AudioService extends IAudioService.Stub if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { return; } + + if (mContext.checkCallingOrSelfPermission( + android.Manifest.permission.MODIFY_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + synchronized (mSetModeDeathHandlers) { + for (SetModeDeathHandler h : mSetModeDeathHandlers) { + if (h.getMode() == AudioSystem.MODE_IN_CALL) { + Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); + return; + } + } + } + } + // for logging only final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(Binder.getCallingUid()).append("/") @@ -4944,6 +4965,10 @@ public class AudioService extends IAudioService.Stub case MSG_ENABLE_SURROUND_FORMATS: onEnableSurroundFormats((ArrayList<Integer>) msg.obj); break; + + case MSG_UPDATE_RINGER_MODE: + onUpdateRingerModeServiceInt(); + break; } } } @@ -5159,6 +5184,20 @@ public class AudioService extends IAudioService.Stub } else if (action.equals(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION) || action.equals(AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION)) { handleAudioEffectBroadcast(context, intent); + } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) { + final int[] suspendedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST); + final String[] suspendedPackages = + intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); + if (suspendedPackages == null || suspendedUids == null + || suspendedPackages.length != suspendedUids.length) { + return; + } + for (int i = 0; i < suspendedUids.length; i++) { + if (!TextUtils.isEmpty(suspendedPackages[i])) { + mMediaFocusControl.noFocusForSuspendedApp( + suspendedPackages[i], suspendedUids[i]); + } + } } } } // end class AudioServiceBroadcastReceiver @@ -5323,6 +5362,11 @@ public class AudioService extends IAudioService.Stub } } + if (callingPackageName == null || clientId == null || aa == null) { + Log.e(TAG, "Invalid null parameter to request audio focus"); + return AudioManager.AUDIOFOCUS_REQUEST_FAILED; + } + return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd, clientId, callingPackageName, flags, sdk, forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid())); diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java index 99f08405a375..db55138e446d 100644 --- a/services/core/java/com/android/server/audio/FocusRequester.java +++ b/services/core/java/com/android/server/audio/FocusRequester.java @@ -45,8 +45,8 @@ public class FocusRequester { private AudioFocusDeathHandler mDeathHandler; // may be null private IAudioFocusDispatcher mFocusDispatcher; // may be null private final IBinder mSourceRef; // may be null - private final String mClientId; - private final String mPackageName; + private final @NonNull String mClientId; + private final @NonNull String mPackageName; private final int mCallingUid; private final MediaFocusControl mFocusController; // never null private final int mSdkTarget; @@ -72,7 +72,7 @@ public class FocusRequester { /** * the audio attributes associated with the focus request */ - private final AudioAttributes mAttributes; + private final @NonNull AudioAttributes mAttributes; /** * Class constructor @@ -87,9 +87,10 @@ public class FocusRequester { * @param uid * @param ctlr cannot be null */ - FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, - IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, - String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) { + FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags, + IAudioFocusDispatcher afl, IBinder source, @NonNull String id, + AudioFocusDeathHandler hdlr, @NonNull String pn, int uid, + @NonNull MediaFocusControl ctlr, int sdk) { mAttributes = aa; mFocusDispatcher = afl; mSourceRef = source; @@ -124,11 +125,7 @@ public class FocusRequester { } boolean hasSameClient(String otherClient) { - try { - return mClientId.compareTo(otherClient) == 0; - } catch (NullPointerException e) { - return false; - } + return mClientId.compareTo(otherClient) == 0; } boolean isLockedFocusOwner() { @@ -143,12 +140,8 @@ public class FocusRequester { return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd); } - boolean hasSamePackage(String pack) { - try { - return mPackageName.compareTo(pack) == 0; - } catch (NullPointerException e) { - return false; - } + boolean hasSamePackage(@NonNull String pack) { + return mPackageName.compareTo(pack) == 0; } boolean hasSameUid(int uid) { diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java index d023bd7827ff..b4bbbc729505 100644 --- a/services/core/java/com/android/server/audio/MediaFocusControl.java +++ b/services/core/java/com/android/server/audio/MediaFocusControl.java @@ -24,7 +24,6 @@ import android.media.AudioFocusInfo; import android.media.AudioManager; import android.media.AudioSystem; import android.media.IAudioFocusDispatcher; -import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.IAudioPolicyCallback; import android.os.Binder; import android.os.Build; @@ -35,6 +34,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -44,7 +44,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.Stack; -import java.text.DateFormat; /** * @hide @@ -138,6 +137,30 @@ public class MediaFocusControl implements PlayerFocusEnforcer { private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, "focus commands as seen by MediaFocusControl"); + /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { + synchronized (mAudioFocusLock) { + final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); + List<String> clientsToRemove = new ArrayList<>(); + while (stackIterator.hasNext()) { + final FocusRequester focusOwner = stackIterator.next(); + if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) { + clientsToRemove.add(focusOwner.getClientId()); + mEventLogger.log((new AudioEventLogger.StringEvent( + "focus owner:" + focusOwner.getClientId() + + " in uid:" + uid + " pack: " + packageName + + " getting AUDIOFOCUS_LOSS due to app suspension")) + .printLog(TAG)); + // make the suspended app lose focus through its focus listener (if any) + focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); + } + } + for (String clientToRemove : clientsToRemove) { + // update the stack but don't signal the change. + removeFocusStackEntry(clientToRemove, false, true); + } + } + } + /** * Discard the current audio focus owner. * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign @@ -688,9 +711,9 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ - protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, - IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, - int sdk, boolean forceDuck) { + protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, + IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, + int flags, int sdk, boolean forceDuck) { mEventLogger.log((new AudioEventLogger.StringEvent( "requestAudioFocus() from uid/pid " + Binder.getCallingUid() + "/" + Binder.getCallingPid() diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index bd4acdbf4070..7d4ac592a9df 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -34,7 +34,7 @@ public abstract class AuthenticationClient extends ClientMonitor { private long mOpId; public abstract int handleFailedAttempt(); - public abstract void resetFailedAttempts(); + public void resetFailedAttempts() {} public static final int LOCKOUT_NONE = 0; public static final int LOCKOUT_TIMED = 1; @@ -42,6 +42,11 @@ public abstract class AuthenticationClient extends ClientMonitor { private final boolean mRequireConfirmation; + // We need to track this state since it's possible for applications to request for + // authentication while the device is already locked out. In that case, the client is created + // but not started yet. The user shouldn't receive the error haptics in this case. + private boolean mStarted; + /** * This method is called when authentication starts. */ @@ -53,6 +58,11 @@ public abstract class AuthenticationClient extends ClientMonitor { */ public abstract void onStop(); + /** + * @return true if the framework should handle lockout. + */ + public abstract boolean shouldFrameworkHandleLockout(); + public AuthenticationClient(Context context, Metrics metrics, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, @@ -91,6 +101,23 @@ public abstract class AuthenticationClient extends ClientMonitor { } @Override + public boolean onError(long deviceId, int error, int vendorCode) { + if (!shouldFrameworkHandleLockout()) { + switch (error) { + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: + case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: + if (mStarted) { + vibrateError(); + } + break; + default: + break; + } + } + return super.onError(deviceId, error, vendorCode); + } + + @Override public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { super.logOnAuthenticated(authenticated, mRequireConfirmation, getTargetUserId(), @@ -113,7 +140,9 @@ public abstract class AuthenticationClient extends ClientMonitor { vibrateSuccess(); } result = true; - resetFailedAttempts(); + if (shouldFrameworkHandleLockout()) { + resetFailedAttempts(); + } onStop(); final byte[] byteToken = new byte[token.size()]; @@ -147,9 +176,10 @@ public abstract class AuthenticationClient extends ClientMonitor { if (listener != null) { vibrateError(); } + // Allow system-defined limit of number of attempts before giving up final int lockoutMode = handleFailedAttempt(); - if (lockoutMode != LOCKOUT_NONE) { + if (lockoutMode != LOCKOUT_NONE && shouldFrameworkHandleLockout()) { Slog.w(getLogTag(), "Forcing lockout (driver code should do this!), mode(" + lockoutMode + ")"); stop(false); @@ -170,7 +200,7 @@ public abstract class AuthenticationClient extends ClientMonitor { } } } - result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode + result = lockoutMode != LOCKOUT_NONE; // in a lockout mode } } catch (RemoteException e) { Slog.e(getLogTag(), "Remote exception", e); @@ -184,6 +214,7 @@ public abstract class AuthenticationClient extends ClientMonitor { */ @Override public int start() { + mStarted = true; onStart(); try { final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); @@ -209,6 +240,8 @@ public abstract class AuthenticationClient extends ClientMonitor { return 0; } + mStarted = false; + onStop(); try { diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index bca84f7b7217..ddd416e14164 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -273,16 +273,13 @@ public class BiometricService extends SystemService { */ private static final int STATE_AUTH_STARTED = 2; /** - * Authentication is paused, waiting for the user to press "try again" button. Since the - * try again button requires us to cancel authentication, this represents the state where - * ERROR_CANCELED is not received yet. + * Authentication is paused, waiting for the user to press "try again" button. Only + * passive modalities such as Face or Iris should have this state. Note that for passive + * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from + * fingerprint. */ private static final int STATE_AUTH_PAUSED = 3; /** - * Same as above, except the ERROR_CANCELED has been received. - */ - private static final int STATE_AUTH_PAUSED_CANCELED = 4; - /** * Authentication is successful, but we're waiting for the user to press "confirm" button. */ private static final int STATE_AUTH_PENDING_CONFIRM = 5; @@ -457,11 +454,6 @@ public class BiometricService extends SystemService { // Pause authentication. onBiometricAuthenticated(false) causes the // dialog to show a "try again" button for passive modalities. mCurrentAuthSession.mState = STATE_AUTH_PAUSED; - // Cancel authentication. Skip the token/package check since we are - // cancelling from system server. The interface is permission protected so - // this is fine. - cancelInternal(null /* token */, null /* package */, - false /* fromClient */); } mCurrentAuthSession.mClientReceiver.onAuthenticationFailed(); @@ -507,24 +499,15 @@ public class BiometricService extends SystemService { } }, BiometricPrompt.HIDE_DIALOG_DELAY); } - } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED) { - if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - && error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) { - // Skip the first ERROR_CANCELED message when this happens, since - // "try again" requires us to cancel authentication but keep - // the prompt showing. - mCurrentAuthSession.mState = STATE_AUTH_PAUSED_CANCELED; - } else { - // In the "try again" state, we should forward canceled errors to - // the client and and clean up. - mCurrentAuthSession.mClientReceiver.onError(error, message); - mStatusBarService.onBiometricError(message); - mActivityTaskManager.unregisterTaskStackListener( - mTaskStackListener); - mCurrentAuthSession.mState = STATE_AUTH_IDLE; - mCurrentAuthSession = null; - } + } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) { + // In the "try again" state, we should forward canceled errors to + // the client and and clean up. + mCurrentAuthSession.mClientReceiver.onError(error, message); + mStatusBarService.onBiometricError(message); + mActivityTaskManager.unregisterTaskStackListener( + mTaskStackListener); + mCurrentAuthSession.mState = STATE_AUTH_IDLE; + mCurrentAuthSession = null; } else { Slog.e(TAG, "Impossible session error state: " + mCurrentAuthSession.mState); @@ -705,8 +688,7 @@ public class BiometricService extends SystemService { if (mPendingAuthSession.mModalitiesWaiting.isEmpty()) { final boolean continuing = mCurrentAuthSession != null && - (mCurrentAuthSession.mState == STATE_AUTH_PAUSED - || mCurrentAuthSession.mState == STATE_AUTH_PAUSED_CANCELED); + (mCurrentAuthSession.mState == STATE_AUTH_PAUSED); mCurrentAuthSession = mPendingAuthSession; mPendingAuthSession = null; @@ -1029,7 +1011,7 @@ public class BiometricService extends SystemService { } @Override // Binder call - public void resetTimeout(byte[] token) { + public void resetLockout(byte[] token) { checkInternalPermission(); final long ident = Binder.clearCallingIdentity(); try { @@ -1037,7 +1019,7 @@ public class BiometricService extends SystemService { mFingerprintService.resetTimeout(token); } if (mFaceService != null) { - mFaceService.resetTimeout(token); + mFaceService.resetLockout(token); } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 9e0f2fcaa29f..92a8d9359b58 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -20,17 +20,12 @@ import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREG import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.IActivityTaskManager; -import android.app.PendingIntent; import android.app.SynchronousUserSwitchObserver; import android.app.TaskStackListener; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -54,14 +49,11 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.util.SparseBooleanArray; -import android.util.SparseIntArray; import android.util.StatsLog; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.server.SystemService; -import com.android.server.biometrics.fingerprint.FingerprintService; import java.util.ArrayList; import java.util.Collections; @@ -80,38 +72,36 @@ public abstract class BiometricServiceBase extends SystemService protected static final boolean DEBUG = true; + private static final boolean CLEANUP_UNKNOWN_TEMPLATES = true; private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user"; private static final int MSG_USER_SWITCHING = 10; - private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000; private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms private final Context mContext; private final String mKeyguardPackage; - private final SparseBooleanArray mTimedLockoutCleared; - private final SparseIntArray mFailedAttempts; private final IActivityTaskManager mActivityTaskManager; - private final AlarmManager mAlarmManager; private final PowerManager mPowerManager; private final UserManager mUserManager; private final MetricsLogger mMetricsLogger; private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener(); private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable(); - private final LockoutReceiver mLockoutReceiver = new LockoutReceiver(); private final ArrayList<LockoutResetMonitor> mLockoutMonitors = new ArrayList<>(); protected final IStatusBarService mStatusBarService; protected final Map<Integer, Long> mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); - protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable = - new ResetFailedAttemptsForUserRunnable(); protected final AppOpsManager mAppOps; protected final H mHandler = new H(); + private final IBinder mToken = new Binder(); // Used for internal enumeration + private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); + private IBiometricService mBiometricService; private ClientMonitor mCurrentClient; private ClientMonitor mPendingClient; private PerformanceStats mPerformanceStats; protected int mCurrentUserId = UserHandle.USER_NULL; + protected long mHalDeviceId; // Tracks if the current authentication makes use of CryptoObjects. protected boolean mIsCrypto; // Normal authentications are tracked by mPerformanceMap. @@ -135,21 +125,14 @@ public abstract class BiometricServiceBase extends SystemService protected abstract String getTag(); /** - * @return the biometric utilities for a specific implementation. - */ - protected abstract BiometricUtils getBiometricUtils(); - - /** - * @return the number of failed attempts after which the user will be temporarily locked out - * from using the biometric. A strong auth (pin/pattern/pass) clears this counter. + * @return wrapper for the HAL */ - protected abstract int getFailedAttemptsLockoutTimed(); + protected abstract DaemonWrapper getDaemonWrapper(); /** - * @return the number of failed attempts after which the user will be permanently locked out - * from using the biometric. A strong auth (pin/pattern/pass) clears this counter. + * @return the biometric utilities for a specific implementation. */ - protected abstract int getFailedAttemptsLockoutPermanent(); + protected abstract BiometricUtils getBiometricUtils(); /** * @return the metrics constants for a biometric implementation. @@ -186,13 +169,6 @@ public abstract class BiometricServiceBase extends SystemService protected abstract long getHalDeviceId(); /** - * This method is called when the user switches. Implementations should probably notify the - * HAL. - * @param userId - */ - protected abstract void handleUserSwitching(int userId); - - /** * @param userId * @return Returns true if the user has any enrolled biometrics. */ @@ -215,6 +191,9 @@ public abstract class BiometricServiceBase extends SystemService */ protected abstract boolean checkAppOps(int uid, String opPackageName); + protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates( + int userId); + /** * Notifies clients of any change in the biometric state (active / idle). This is mainly for * Fingerprint navigation gestures. @@ -224,6 +203,11 @@ public abstract class BiometricServiceBase extends SystemService protected abstract int statsModality(); + /** + * @return one of the AuthenticationClient LOCKOUT constants + */ + protected abstract int getLockoutMode(); + protected abstract class AuthenticationClientImpl extends AuthenticationClient { // Used to check if the public API that was invoked was from FingerprintManager. Only @@ -271,21 +255,12 @@ public abstract class BiometricServiceBase extends SystemService } @Override - public void resetFailedAttempts() { - resetFailedAttemptsForUser(true /* clearAttemptCounter */, - ActivityManager.getCurrentUser()); - } - - @Override public void notifyUserActivity() { userActivity(); } @Override public int handleFailedAttempt() { - final int currentUser = ActivityManager.getCurrentUser(); - mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); - mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false); final int lockoutMode = getLockoutMode(); if (lockoutMode == AuthenticationClient.LOCKOUT_PERMANENT) { mPerformanceStats.permanentLockout++; @@ -295,7 +270,6 @@ public abstract class BiometricServiceBase extends SystemService // Failing multiple times will continue to push out the lockout time if (lockoutMode != AuthenticationClient.LOCKOUT_NONE) { - scheduleLockoutResetForUser(currentUser); return lockoutMode; } return AuthenticationClient.LOCKOUT_NONE; @@ -319,40 +293,106 @@ public abstract class BiometricServiceBase extends SystemService } } - protected abstract class RemovalClientImpl extends RemovalClient { - private boolean mShouldNotify; - - public RemovalClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int fingerId, int groupId, int userId, + /** + * An internal class to help clean up unknown templates in HAL and Framework + */ + private final class InternalRemovalClient extends RemovalClient { + InternalRemovalClient(Context context, + DaemonWrapper daemon, long halDeviceId, IBinder token, + ServiceListener listener, int templateId, int groupId, int userId, boolean restricted, String owner) { - super(context, getMetrics(), daemon, halDeviceId, token, listener, fingerId, groupId, + super(context, getMetrics(), daemon, halDeviceId, token, listener, templateId, groupId, userId, restricted, owner, getBiometricUtils()); } - public void setShouldNotifyUserActivity(boolean shouldNotify) { - mShouldNotify = shouldNotify; - } - @Override - public void notifyUserActivity() { - if (mShouldNotify) { - userActivity(); - } + protected int statsModality() { + return BiometricServiceBase.this.statsModality(); } } - protected abstract class EnumerateClientImpl extends EnumerateClient { - - public EnumerateClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int groupId, int userId, - boolean restricted, String owner) { + /** + * Internal class to help clean up unknown templates in the HAL and Framework + */ + private final class InternalEnumerateClient extends EnumerateClient { + + private BiometricUtils mUtils; + // List of templates that are known to the Framework. Remove from this list when enumerate + // returns a template that contains a match. + private List<? extends BiometricAuthenticator.Identifier> mEnrolledList; + // List of templates to remove from the HAL + private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>(); + + InternalEnumerateClient(Context context, + DaemonWrapper daemon, long halDeviceId, IBinder token, + ServiceListener listener, int groupId, int userId, boolean restricted, + String owner, List<? extends BiometricAuthenticator.Identifier> enrolledList, + BiometricUtils utils) { super(context, getMetrics(), daemon, halDeviceId, token, listener, groupId, userId, restricted, owner); + mEnrolledList = enrolledList; + mUtils = utils; + } + + private void handleEnumeratedTemplate(BiometricAuthenticator.Identifier identifier) { + if (identifier == null) { + return; + } + Slog.v(getTag(), "handleEnumeratedTemplate: " + identifier.getBiometricId()); + boolean matched = false; + for (int i = 0; i < mEnrolledList.size(); i++) { + if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) { + mEnrolledList.remove(i); + matched = true; + break; + } + } + + // TemplateId 0 means no templates in HAL + if (!matched && identifier.getBiometricId() != 0) { + mUnknownHALTemplates.add(identifier); + } + Slog.v(getTag(), "Matched: " + matched); + } + + private void doTemplateCleanup() { + if (mEnrolledList == null) { + return; + } + + // At this point, mEnrolledList only contains templates known to the framework and + // not the HAL. + for (int i = 0; i < mEnrolledList.size(); i++) { + BiometricAuthenticator.Identifier identifier = mEnrolledList.get(i); + Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: " + + identifier.getBiometricId() + " " + + identifier.getName()); + mUtils.removeBiometricForUser(getContext(), + getTargetUserId(), identifier.getBiometricId()); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); + } + mEnrolledList.clear(); + } + + public List<BiometricAuthenticator.Identifier> getUnknownHALTemplates() { + return mUnknownHALTemplates; } @Override - public void notifyUserActivity() { - userActivity(); + public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, + int remaining) { + handleEnumeratedTemplate(identifier); + if (remaining == 0) { + doTemplateCleanup(); + } + return remaining == 0; + } + + @Override + protected int statsModality() { + return BiometricServiceBase.this.statsModality(); } } @@ -429,13 +469,14 @@ public abstract class BiometricServiceBase extends SystemService * subclasses. */ protected interface DaemonWrapper { - int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. see errno.h. + int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h. int authenticate(long operationId, int groupId) throws RemoteException; int cancel() throws RemoteException; int remove(int groupId, int biometricId) throws RemoteException; int enumerate() throws RemoteException; - int enroll(byte[] cryptoToken, int groupId, int timeout, + int enroll(byte[] token, int groupId, int timeout, ArrayList<Integer> disabledFeatures) throws RemoteException; + void resetLockout(byte[] token) throws RemoteException; } /** @@ -506,24 +547,7 @@ public abstract class BiometricServiceBase extends SystemService } } - private final class LockoutReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Slog.v(getTag(), "Resetting lockout: " + intent.getAction()); - if (getLockoutResetIntent().equals(intent.getAction())) { - final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0); - resetFailedAttemptsForUser(false /* clearAttemptCounter */, user); - } - } - } - private final class ResetFailedAttemptsForUserRunnable implements Runnable { - @Override - public void run() { - resetFailedAttemptsForUser(true /* clearAttemptCounter */, - ActivityManager.getCurrentUser()); - } - } private final class LockoutResetMonitor implements IBinder.DeathRecipient { private static final long WAKELOCK_TIMEOUT_MS = 2000; @@ -583,6 +607,19 @@ public abstract class BiometricServiceBase extends SystemService } /** + * Container for enumerated templates. Used to keep track when cleaning up unknown + * templates. + */ + private final class UserTemplate { + final BiometricAuthenticator.Identifier mIdentifier; + final int mUserId; + UserTemplate(BiometricAuthenticator.Identifier identifier, int userId) { + this.mIdentifier = identifier; + this.mUserId = userId; + } + } + + /** * Initializes the system service. * <p> * Subclasses must define a single argument constructor that accepts the context @@ -599,16 +636,11 @@ public abstract class BiometricServiceBase extends SystemService mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString( com.android.internal.R.string.config_keyguardComponent)).getPackageName(); mAppOps = context.getSystemService(AppOpsManager.class); - mTimedLockoutCleared = new SparseBooleanArray(); - mFailedAttempts = new SparseIntArray(); mActivityTaskManager = ((ActivityTaskManager) context.getSystemService( Context.ACTIVITY_TASK_SERVICE)).getService(); mPowerManager = mContext.getSystemService(PowerManager.class); - mAlarmManager = mContext.getSystemService(AlarmManager.class); mUserManager = UserManager.get(mContext); mMetricsLogger = new MetricsLogger(); - mContext.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()), - getLockoutBroadcastPermission(), null /* handler */); } @Override @@ -688,6 +720,11 @@ public abstract class BiometricServiceBase extends SystemService if (DEBUG) Slog.v(getTag(), "handleError(client=" + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")"); + if (client instanceof InternalRemovalClient + || client instanceof InternalEnumerateClient) { + clearEnumerateState(); + } + if (client != null && client.onError(deviceId, error, vendorCode)) { removeClient(client); } @@ -721,6 +758,39 @@ public abstract class BiometricServiceBase extends SystemService updateActiveGroup(userId, null); } } + + if (client instanceof InternalRemovalClient && !mUnknownHALTemplates.isEmpty()) { + startCleanupUnknownHALTemplates(); + } else if (client instanceof InternalRemovalClient) { + clearEnumerateState(); + } + } + + protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) { + ClientMonitor client = getCurrentClient(); + + client.onEnumerationResult(identifier, remaining); + + // All templates in the HAL for this user were enumerated + if (remaining == 0) { + if (client instanceof InternalEnumerateClient) { + List<BiometricAuthenticator.Identifier> unknownHALTemplates = + ((InternalEnumerateClient) client).getUnknownHALTemplates(); + + if (!unknownHALTemplates.isEmpty()) { + Slog.w(getTag(), "Adding " + unknownHALTemplates.size() + + " templates for deletion"); + } + for (int i = 0; i < unknownHALTemplates.size(); i++) { + mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i), + client.getTargetUserId())); + } + removeClient(client); + startCleanupUnknownHALTemplates(); + } else { + removeClient(client); + } + } } /** @@ -832,13 +902,13 @@ public abstract class BiometricServiceBase extends SystemService }); } - protected void removeInternal(RemovalClientImpl client) { + protected void removeInternal(RemovalClient client) { mHandler.post(() -> { startClient(client, true /* initiatedByClient */); }); } - protected void enumerateInternal(EnumerateClientImpl client) { + protected void enumerateInternal(EnumerateClient client) { mHandler.post(() -> { startClient(client, true /* initiatedByClient */); }); @@ -919,19 +989,6 @@ public abstract class BiometricServiceBase extends SystemService return mKeyguardPackage.equals(clientPackage); } - protected int getLockoutMode() { - final int currentUser = ActivityManager.getCurrentUser(); - final int failedAttempts = mFailedAttempts.get(currentUser, 0); - if (failedAttempts >= getFailedAttemptsLockoutPermanent()) { - return AuthenticationClient.LOCKOUT_PERMANENT; - } else if (failedAttempts > 0 && - mTimedLockoutCleared.get(currentUser, false) == false - && (failedAttempts % getFailedAttemptsLockoutTimed() == 0)) { - return AuthenticationClient.LOCKOUT_TIMED; - } - return AuthenticationClient.LOCKOUT_NONE; - } - private boolean isForegroundActivity(int uid, int pid) { try { List<ActivityManager.RunningAppProcessInfo> procs = @@ -965,10 +1022,10 @@ public abstract class BiometricServiceBase extends SystemService currentClient.getOwnerString()); // This check only matters for FingerprintService, since enumerate may call back // multiple times. - if (currentClient instanceof FingerprintService.EnumerateClientImpl || - currentClient instanceof FingerprintService.RemovalClientImpl) { + if (currentClient instanceof InternalEnumerateClient + || currentClient instanceof InternalRemovalClient) { // This condition means we're currently running internal diagnostics to - // remove extra fingerprints in the hardware and/or the software + // remove extra templates in the hardware and/or the software // TODO: design an escape hatch in case client never finishes if (newClient != null) { Slog.w(getTag(), "Internal cleanup in progress but trying to start client " @@ -1124,16 +1181,75 @@ public abstract class BiometricServiceBase extends SystemService return mAuthenticatorIds.getOrDefault(userId, 0L); } - private void scheduleLockoutResetForUser(int userId) { - mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, - getLockoutResetIntentForUser(userId)); + /** + * This method should be called upon connection to the daemon, and when user switches. + * @param userId + */ + protected void doTemplateCleanupForUser(int userId) { + if (CLEANUP_UNKNOWN_TEMPLATES) { + enumerateUser(userId); + } } - private PendingIntent getLockoutResetIntentForUser(int userId) { - return PendingIntent.getBroadcast(mContext, userId, - new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId), - PendingIntent.FLAG_UPDATE_CURRENT); + private void clearEnumerateState() { + if (DEBUG) Slog.v(getTag(), "clearEnumerateState()"); + mUnknownHALTemplates.clear(); + } + + /** + * Remove unknown templates from HAL + */ + private void startCleanupUnknownHALTemplates() { + if (!mUnknownHALTemplates.isEmpty()) { + UserTemplate template = mUnknownHALTemplates.get(0); + mUnknownHALTemplates.remove(template); + boolean restricted = !hasPermission(getManageBiometricPermission()); + InternalRemovalClient client = new InternalRemovalClient(getContext(), + getDaemonWrapper(), mHalDeviceId, mToken, null /* listener */, + template.mIdentifier.getBiometricId(), 0 /* groupId */, template.mUserId, + restricted, getContext().getPackageName()); + removeInternal(client); + StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); + } else { + clearEnumerateState(); + } + } + + private void enumerateUser(int userId) { + if (DEBUG) Slog.v(getTag(), "Enumerating user(" + userId + ")"); + + final boolean restricted = !hasPermission(getManageBiometricPermission()); + final List<? extends BiometricAuthenticator.Identifier> enrolledList = + getEnrolledTemplates(userId); + + InternalEnumerateClient client = new InternalEnumerateClient(getContext(), + getDaemonWrapper(), mHalDeviceId, mToken, null /* serviceListener */, userId, + userId, restricted, getContext().getOpPackageName(), enrolledList, + getBiometricUtils()); + enumerateInternal(client); + } + + /** + * This method is called when the user switches. Implementations should probably notify the + * HAL. + */ + protected void handleUserSwitching(int userId) { + if (getCurrentClient() instanceof InternalRemovalClient + || getCurrentClient() instanceof InternalEnumerateClient) { + Slog.w(getTag(), "User switched while performing cleanup"); + removeClient(getCurrentClient()); + clearEnumerateState(); + } + updateActiveGroup(userId, null); + doTemplateCleanupForUser(userId); + } + + protected void notifyLockoutResetMonitors() { + for (int i = 0; i < mLockoutMonitors.size(); i++) { + mLockoutMonitors.get(i).sendLockoutReset(); + } } private void userActivity() { @@ -1169,25 +1285,6 @@ public abstract class BiometricServiceBase extends SystemService return userId; } - // Attempt counter should only be cleared when Keyguard goes away or when - // a biometric is successfully authenticated. - private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) { - if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { - Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter); - } - if (clearAttemptCounter) { - mFailedAttempts.put(userId, 0); - } - mTimedLockoutCleared.put(userId, true); - // If we're asked to reset failed attempts externally (i.e. from Keyguard), - // the alarm might still be pending; remove it. - cancelLockoutResetForUser(userId); - notifyLockoutResetMonitors(); - } - - private void cancelLockoutResetForUser(int userId) { - mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); - } private void listenForUserSwitches() { try { @@ -1204,12 +1301,6 @@ public abstract class BiometricServiceBase extends SystemService } } - private void notifyLockoutResetMonitors() { - for (int i = 0; i < mLockoutMonitors.size(); i++) { - mLockoutMonitors.get(i).sendLockoutReset(); - } - } - private void removeLockoutResetCallback( LockoutResetMonitor monitor) { mLockoutMonitors.remove(monitor); diff --git a/services/core/java/com/android/server/biometrics/EnumerateClient.java b/services/core/java/com/android/server/biometrics/EnumerateClient.java index 0f57f484bb0a..44ac0373507a 100644 --- a/services/core/java/com/android/server/biometrics/EnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/EnumerateClient.java @@ -39,6 +39,10 @@ public abstract class EnumerateClient extends ClientMonitor { } @Override + public void notifyUserActivity() { + } + + @Override protected int statsAction() { return BiometricsProtoEnums.ACTION_ENUMERATE; } @@ -94,7 +98,9 @@ public abstract class EnumerateClient extends ClientMonitor { public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, int remaining) { try { - getListener().onEnumerated(identifier, remaining); + if (getListener() != null) { + getListener().onEnumerated(identifier, remaining); + } } catch (RemoteException e) { Slog.w(getLogTag(), "Failed to notify enumerated:", e); } diff --git a/services/core/java/com/android/server/biometrics/RemovalClient.java b/services/core/java/com/android/server/biometrics/RemovalClient.java index 0509067b4daf..a18f336f3ef4 100644 --- a/services/core/java/com/android/server/biometrics/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/RemovalClient.java @@ -44,6 +44,10 @@ public abstract class RemovalClient extends ClientMonitor { } @Override + public void notifyUserActivity() { + } + + @Override protected int statsAction() { return BiometricsProtoEnums.ACTION_REMOVE; } @@ -95,7 +99,9 @@ public abstract class RemovalClient extends ClientMonitor { private boolean sendRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { try { - getListener().onRemoved(identifier, remaining); + if (getListener() != null) { + getListener().onRemoved(identifier, remaining); + } } catch (RemoteException e) { Slog.w(getLogTag(), "Failed to notify Removed:", e); } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index 8995068ef504..d2d14829d597 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -51,9 +51,13 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; +import com.android.server.biometrics.ClientMonitor; +import com.android.server.biometrics.EnumerateClient; import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; import org.json.JSONException; @@ -79,8 +83,6 @@ public class FaceService extends BiometricServiceBase { private static final String FACE_DATA_DIR = "facedata"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET"; - private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 3; - private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 12; private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes private final class FaceAuthClient extends AuthenticationClientImpl { @@ -96,6 +98,25 @@ public class FaceService extends BiometricServiceBase { protected int statsModality() { return FaceService.this.statsModality(); } + + @Override + public boolean shouldFrameworkHandleLockout() { + return false; + } + + @Override + public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier, + boolean authenticated, ArrayList<Byte> token) { + final boolean result = super.onAuthenticated(identifier, authenticated, token); + + // For face, the authentication lifecycle ends either when + // 1) Authenticated == true + // 2) Error occurred + // 3) Authenticated == false + // Fingerprint currently does not end when the third condition is met which is a bug, + // but let's leave it as-is for now. + return result || !authenticated; + } } /** @@ -106,6 +127,7 @@ public class FaceService extends BiometricServiceBase { /** * The following methods contain common code which is shared in biometrics/common. */ + @Override // Binder call public long generateChallenge(IBinder token) { checkPermission(MANAGE_BIOMETRIC); @@ -216,15 +238,14 @@ public class FaceService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, 0 /* groupId */, - userId, restricted, token.toString()) { + final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), faceId, + 0 /* groupId */, userId, restricted, token.toString(), getBiometricUtils()) { @Override protected int statsModality() { return FaceService.this.statsModality(); } }; - client.setShouldNotifyUserActivity(true); removeInternal(client); } @@ -234,9 +255,9 @@ public class FaceService extends BiometricServiceBase { checkPermission(MANAGE_BIOMETRIC); final boolean restricted = isRestricted(); - final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, - restricted, getContext().getOpPackageName()) { + final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, + userId, restricted, getContext().getOpPackageName()) { @Override protected int statsModality() { return FaceService.this.statsModality(); @@ -317,7 +338,7 @@ public class FaceService extends BiometricServiceBase { return null; } - return FaceService.this.getEnrolledFaces(userId); + return FaceService.this.getEnrolledTemplates(userId); } @Override // Binder call @@ -354,10 +375,13 @@ public class FaceService extends BiometricServiceBase { } @Override // Binder call - public void resetTimeout(byte[] token) { + public void resetLockout(byte[] token) { checkPermission(MANAGE_BIOMETRIC); - // TODO: confirm security token when we move timeout management into the HAL layer. - mHandler.post(mResetFailedAttemptsForCurrentUserRunnable); + try { + mDaemonWrapper.resetLockout(token); + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to reset lockout", e); + } } @Override @@ -511,7 +535,8 @@ public class FaceService extends BiometricServiceBase { public void onEnumerated(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFaceServiceReceiver != null) { - + mFaceServiceReceiver.onEnumerated(identifier.getDeviceId(), + identifier.getBiometricId(), remaining); } } } @@ -520,75 +545,107 @@ public class FaceService extends BiometricServiceBase { @GuardedBy("this") private IBiometricsFace mDaemon; - private long mHalDeviceId; + // One of the AuthenticationClient constants + private int mCurrentUserLockoutMode; /** * Receives callbacks from the HAL. */ private IBiometricsFaceClientCallback mDaemonCallback = new IBiometricsFaceClientCallback.Stub() { - @Override - public void onEnrollResult(final long deviceId, int faceId, int userId, - int remaining) { - mHandler.post(() -> { - final Face face = new Face(getBiometricUtils() - .getUniqueName(getContext(), userId), faceId, deviceId); - FaceService.super.handleEnrollResult(face, remaining); - }); - } + @Override + public void onEnrollResult(final long deviceId, int faceId, int userId, + int remaining) { + mHandler.post(() -> { + final Face face = new Face(getBiometricUtils() + .getUniqueName(getContext(), userId), faceId, deviceId); + FaceService.super.handleEnrollResult(face, remaining); + }); + } - @Override - public void onAcquired(final long deviceId, final int userId, - final int acquiredInfo, - final int vendorCode) { - mHandler.post(() -> { - FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); - }); - } + @Override + public void onAcquired(final long deviceId, final int userId, + final int acquiredInfo, + final int vendorCode) { + mHandler.post(() -> { + FaceService.super.handleAcquired(deviceId, acquiredInfo, vendorCode); + }); + } - @Override - public void onAuthenticated(final long deviceId, final int faceId, final int userId, - ArrayList<Byte> token) { - mHandler.post(() -> { - Face face = new Face("", faceId, deviceId); - FaceService.super.handleAuthenticated(face, token); - }); - } + @Override + public void onAuthenticated(final long deviceId, final int faceId, final int userId, + ArrayList<Byte> token) { + mHandler.post(() -> { + Face face = new Face("", faceId, deviceId); + FaceService.super.handleAuthenticated(face, token); + }); + } - @Override - public void onError(final long deviceId, final int userId, final int error, - final int vendorCode) { - mHandler.post(() -> { - FaceService.super.handleError(deviceId, error, vendorCode); - - // TODO: this chunk of code should be common to all biometric services - if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { - // If we get HW_UNAVAILABLE, try to connect again later... - Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); - synchronized (this) { - mDaemon = null; - mHalDeviceId = 0; - mCurrentUserId = UserHandle.USER_NULL; - } - } - }); + @Override + public void onError(final long deviceId, final int userId, final int error, + final int vendorCode) { + mHandler.post(() -> { + FaceService.super.handleError(deviceId, error, vendorCode); + + // TODO: this chunk of code should be common to all biometric services + if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { + // If we get HW_UNAVAILABLE, try to connect again later... + Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client."); + synchronized (this) { + mDaemon = null; + mHalDeviceId = 0; + mCurrentUserId = UserHandle.USER_NULL; + } } + }); + } - @Override - public void onRemoved(final long deviceId, final int faceId, final int userId, - final int remaining) { - mHandler.post(() -> { - final Face face = new Face("", faceId, deviceId); - FaceService.super.handleRemoved(face, remaining); - }); + @Override + public void onRemoved(final long deviceId, final int faceId, final int userId, + final int remaining) { + mHandler.post(() -> { + final Face face = new Face("", faceId, deviceId); + FaceService.super.handleRemoved(face, remaining); + }); + } + + @Override + public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) + throws RemoteException { + mHandler.post(() -> { + if (!faceIds.isEmpty()) { + for (int i = 0; i < faceIds.size(); i++) { + final Face face = new Face("", faceIds.get(i), deviceId); + // Convert to old old behavior + FaceService.super.handleEnumerate(face, faceIds.size() - i - 1); + } + } else { + // For face, the HIDL contract is to receive an empty list when there are no + // templates enrolled. Send a null identifier since we don't consume them + // anywhere, and send remaining == 0 to plumb this with existing common code. + FaceService.super.handleEnumerate(null /* identifier */, 0); } + }); + } - @Override - public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) - throws RemoteException { - // TODO + @Override + public void onLockoutChanged(long duration) { + Slog.d(TAG, "onLockoutChanged: " + duration); + if (duration == 0) { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; + } else if (duration == Long.MAX_VALUE) { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_PERMANENT; + } else { + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_TIMED; + } + + mHandler.post(() -> { + if (duration == 0) { + notifyLockoutResetMonitors(); } - }; + }); + } + }; /** * Wraps the HAL-specific code and is passed to the ClientMonitor implementations so that they @@ -647,9 +704,22 @@ public class FaceService extends BiometricServiceBase { for (int i = 0; i < cryptoToken.length; i++) { token.add(cryptoToken[i]); } - // TODO: plumb requireAttention down from framework return daemon.enroll(token, timeout, disabledFeatures); } + + @Override + public void resetLockout(byte[] cryptoToken) throws RemoteException { + IBiometricsFace daemon = getFaceDaemon(); + if (daemon == null) { + Slog.w(TAG, "resetLockout(): no face HAL!"); + return; + } + final ArrayList<Byte> token = new ArrayList<>(); + for (int i = 0; i < cryptoToken.length; i++) { + token.add(cryptoToken[i]); + } + daemon.resetLockout(token); + } }; @@ -670,18 +740,13 @@ public class FaceService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { - return FaceUtils.getInstance(); - } - - @Override - protected int getFailedAttemptsLockoutTimed() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED; + protected DaemonWrapper getDaemonWrapper() { + return mDaemonWrapper; } @Override - protected int getFailedAttemptsLockoutPermanent() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT; + protected BiometricUtils getBiometricUtils() { + return FaceUtils.getInstance(); } @Override @@ -693,7 +758,7 @@ public class FaceService extends BiometricServiceBase { protected boolean hasReachedEnrollmentLimit(int userId) { final int limit = getContext().getResources().getInteger( com.android.internal.R.integer.config_faceMaxTemplatesPerUser); - final int enrolled = FaceService.this.getEnrolledFaces(userId).size(); + final int enrolled = FaceService.this.getEnrolledTemplates(userId).size(); if (enrolled >= limit) { Slog.w(TAG, "Too many faces registered"); return true; @@ -761,7 +826,9 @@ public class FaceService extends BiometricServiceBase { @Override protected void handleUserSwitching(int userId) { - updateActiveGroup(userId, null); + super.handleUserSwitching(userId); + // Will be updated when we get the callback from HAL + mCurrentUserLockoutMode = AuthenticationClient.LOCKOUT_NONE; } @Override @@ -780,7 +847,6 @@ public class FaceService extends BiometricServiceBase { @Override protected void checkUseBiometricPermission() { // noop for Face. The permission checks are all done on the incoming binder call. - // TODO: Perhaps do the same in FingerprintService } @Override @@ -790,6 +856,11 @@ public class FaceService extends BiometricServiceBase { } @Override + protected List<Face> getEnrolledTemplates(int userId) { + return getBiometricUtils().getBiometricsForUser(getContext(), userId); + } + + @Override protected void notifyClientActiveCallbacks(boolean isActive) { // noop for Face. } @@ -799,6 +870,11 @@ public class FaceService extends BiometricServiceBase { return BiometricsProtoEnums.MODALITY_FACE; } + @Override + protected int getLockoutMode() { + return mCurrentUserLockoutMode; + } + /** Gets the face daemon */ private synchronized IBiometricsFace getFaceDaemon() { if (mDaemon == null) { @@ -828,6 +904,7 @@ public class FaceService extends BiometricServiceBase { if (mHalDeviceId != 0) { loadAuthenticatorIds(); updateActiveGroup(ActivityManager.getCurrentUser(), null); + doTemplateCleanupForUser(ActivityManager.getCurrentUser()); } else { Slog.w(TAG, "Failed to open Face HAL!"); MetricsLogger.count(getContext(), "faced_openhal_error", 1); @@ -865,10 +942,6 @@ public class FaceService extends BiometricServiceBase { return 0; } - private List<Face> getEnrolledFaces(int userId) { - return getBiometricUtils().getBiometricsForUser(getContext(), userId); - } - private void dumpInternal(PrintWriter pw) { JSONObject dump = new JSONObject(); try { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index d8544e324618..164468e99967 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -24,8 +24,13 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_FINGERPRINT; import android.app.ActivityManager; +import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; @@ -46,21 +51,25 @@ import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; import android.os.SELinux; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; -import android.util.StatsLog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.DumpUtils; import com.android.server.SystemServerInitThreadPool; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.ClientMonitor; import com.android.server.biometrics.EnumerateClient; import com.android.server.biometrics.Metrics; +import com.android.server.biometrics.RemovalClient; import org.json.JSONArray; import org.json.JSONException; @@ -85,20 +94,30 @@ public class FingerprintService extends BiometricServiceBase { protected static final String TAG = "FingerprintService"; private static final boolean DEBUG = true; - private static final boolean CLEANUP_UNUSED_FP = true; private static final String FP_DATA_DIR = "fpdata"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.fingerprint.ACTION_LOCKOUT_RESET"; private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5; private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20; + private static final long FAIL_LOCKOUT_TIMEOUT_MS = 30 * 1000; + private static final String KEY_LOCKOUT_RESET_USER = "lockout_reset_user"; - // TODO: This should be refactored into BiometricService - private final class UserFingerprint { - Fingerprint f; - int userId; - public UserFingerprint(Fingerprint f, int userId) { - this.f = f; - this.userId = userId; + private final class ResetFailedAttemptsForUserRunnable implements Runnable { + @Override + public void run() { + resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); + } + } + + private final class LockoutReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Slog.v(getTag(), "Resetting lockout: " + intent.getAction()); + if (getLockoutResetIntent().equals(intent.getAction())) { + final int user = intent.getIntExtra(KEY_LOCKOUT_RESET_USER, 0); + resetFailedAttemptsForUser(false /* clearAttemptCounter */, user); + } } } @@ -121,6 +140,30 @@ public class FingerprintService extends BiometricServiceBase { protected int statsModality() { return FingerprintService.this.statsModality(); } + + @Override + public void resetFailedAttempts() { + resetFailedAttemptsForUser(true /* clearAttemptCounter */, + ActivityManager.getCurrentUser()); + } + + @Override + public boolean shouldFrameworkHandleLockout() { + return true; + } + + @Override + public int handleFailedAttempt() { + final int currentUser = ActivityManager.getCurrentUser(); + mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1); + mTimedLockoutCleared.put(ActivityManager.getCurrentUser(), false); + + if (getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { + scheduleLockoutResetForUser(currentUser); + } + + return super.handleFailedAttempt(); + } } /** @@ -241,15 +284,14 @@ public class FingerprintService extends BiometricServiceBase { } final boolean restricted = isRestricted(); - final RemovalClientImpl client = new RemovalClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), fingerId, groupId, - userId, restricted, token.toString()) { + final RemovalClient client = new RemovalClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), + fingerId, groupId, userId, restricted, token.toString(), getBiometricUtils()) { @Override protected int statsModality() { return FingerprintService.this.statsModality(); } }; - client.setShouldNotifyUserActivity(true); removeInternal(client); } @@ -259,9 +301,9 @@ public class FingerprintService extends BiometricServiceBase { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); - final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper, - mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, userId, - restricted, getContext().getOpPackageName()) { + final EnumerateClient client = new EnumerateClient(getContext(), getMetrics(), + mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), userId, + userId, restricted, getContext().getOpPackageName()) { @Override protected int statsModality() { return FingerprintService.this.statsModality(); @@ -339,7 +381,7 @@ public class FingerprintService extends BiometricServiceBase { return Collections.emptyList(); } - return FingerprintService.this.getEnrolledFingerprints(userId); + return FingerprintService.this.getEnrolledTemplates(userId); } @Override // Binder call @@ -445,7 +487,6 @@ public class FingerprintService extends BiometricServiceBase { public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFingerprintServiceReceiver != null) { - // TODO: Pass up the fp directly instead final Fingerprint fp = (Fingerprint) identifier; mFingerprintServiceReceiver.onEnrollResult(fp.getDeviceId(), fp.getBiometricId(), fp.getGroupId(), remaining); @@ -493,7 +534,6 @@ public class FingerprintService extends BiometricServiceBase { public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) throws RemoteException { if (mFingerprintServiceReceiver != null) { - // TODO: Pass up the fp directly instead final Fingerprint fp = (Fingerprint) identifier; mFingerprintServiceReceiver.onRemoved(fp.getDeviceId(), fp.getBiometricId(), fp.getGroupId(), remaining); @@ -511,105 +551,18 @@ public class FingerprintService extends BiometricServiceBase { } } - /** - * An internal class to help clean up unknown fingerprints in the hardware and software. - */ - private final class InternalEnumerateClient extends BiometricServiceBase.EnumerateClientImpl { - - private List<Fingerprint> mEnrolledList; - private List<Fingerprint> mUnknownFingerprints = new ArrayList<>(); // list of fp to delete - - public InternalEnumerateClient(Context context, DaemonWrapper daemon, long halDeviceId, - IBinder token, ServiceListener listener, int groupId, int userId, - boolean restricted, String owner, List<Fingerprint> enrolledList) { - super(context, daemon, halDeviceId, token, listener, groupId, userId, restricted, - owner); - mEnrolledList = enrolledList; - } - - private void handleEnumeratedFingerprint( - BiometricAuthenticator.Identifier identifier, int remaining) { - boolean matched = false; - for (int i = 0; i < mEnrolledList.size(); i++) { - if (mEnrolledList.get(i).getBiometricId() == identifier.getBiometricId()) { - mEnrolledList.remove(i); - matched = true; - break; - } - } - - // fingerId 0 means no fingerprints are in hardware - if (!matched && identifier.getBiometricId() != 0) { - mUnknownFingerprints.add((Fingerprint) identifier); - } - } - - private void doFingerprintCleanup() { - if (mEnrolledList == null) { - return; - } - - for (Fingerprint f : mEnrolledList) { - Slog.e(TAG, "doFingerprintCleanup(): Removing dangling enrolled fingerprint: " - + f.getName() + " " + f.getBiometricId() + " " + f.getGroupId() - + " " + f.getDeviceId()); - FingerprintUtils.getInstance().removeBiometricForUser(getContext(), - getTargetUserId(), f.getBiometricId()); - StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, - BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); - } - mEnrolledList.clear(); - } - - public List<Fingerprint> getUnknownFingerprints() { - return mUnknownFingerprints; - } - - @Override - public boolean onEnumerationResult(BiometricAuthenticator.Identifier identifier, - int remaining) { - handleEnumeratedFingerprint(identifier, remaining); - if (remaining == 0) { - doFingerprintCleanup(); - } - return remaining == 0; - } - - @Override - protected int statsModality() { - return FingerprintService.this.statsModality(); - } - } - - /** - * An internal class to help clean up unknown fingerprints in hardware and software. - */ - private final class InternalRemovalClient extends BiometricServiceBase.RemovalClientImpl { - public InternalRemovalClient(Context context, - DaemonWrapper daemon, long halDeviceId, IBinder token, - ServiceListener listener, int fingerId, int groupId, int userId, boolean restricted, - String owner) { - super(context, daemon, halDeviceId, token, listener, fingerId, groupId, userId, - restricted, - owner); - } - - @Override - protected int statsModality() { - return FingerprintService.this.statsModality(); - } - } - private final FingerprintMetrics mFingerprintMetrics = new FingerprintMetrics(); private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks = new CopyOnWriteArrayList<>(); @GuardedBy("this") private IBiometricsFingerprint mDaemon; - - private long mHalDeviceId; - private IBinder mToken = new Binder(); // used for internal FingerprintService enumeration - private ArrayList<UserFingerprint> mUnknownFingerprints = new ArrayList<>(); // hw fingerprints + private final SparseBooleanArray mTimedLockoutCleared; + private final SparseIntArray mFailedAttempts; + private final AlarmManager mAlarmManager; + private final LockoutReceiver mLockoutReceiver = new LockoutReceiver(); + protected final ResetFailedAttemptsForUserRunnable mResetFailedAttemptsForCurrentUserRunnable = + new ResetFailedAttemptsForUserRunnable(); /** * Receives callbacks from the HAL. @@ -646,13 +599,7 @@ public class FingerprintService extends BiometricServiceBase { @Override public void onError(final long deviceId, final int error, final int vendorCode) { mHandler.post(() -> { - ClientMonitor client = getCurrentClient(); - if (client instanceof InternalRemovalClient - || client instanceof InternalEnumerateClient) { - clearEnumerateState(); - } FingerprintService.super.handleError(deviceId, error, vendorCode); - // TODO: this chunk of code should be common to all biometric services if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) { // If we get HW_UNAVAILABLE, try to connect again later... @@ -673,11 +620,6 @@ public class FingerprintService extends BiometricServiceBase { ClientMonitor client = getCurrentClient(); final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); FingerprintService.super.handleRemoved(fp, remaining); - if (client instanceof InternalRemovalClient && !mUnknownFingerprints.isEmpty()) { - cleanupUnknownFingerprints(); - } else if (client instanceof InternalRemovalClient){ - clearEnumerateState(); - } }); } @@ -685,9 +627,8 @@ public class FingerprintService extends BiometricServiceBase { public void onEnumerate(final long deviceId, final int fingerId, final int groupId, final int remaining) { mHandler.post(() -> { - // TODO: factor out common enumerate logic if possible final Fingerprint fp = new Fingerprint("", groupId, fingerId, deviceId); - FingerprintService.this.handleEnumerate(fp, remaining); + FingerprintService.super.handleEnumerate(fp, remaining); }); } @@ -748,10 +689,22 @@ public class FingerprintService extends BiometricServiceBase { } return daemon.enroll(cryptoToken, groupId, timeout); } + + @Override + public void resetLockout(byte[] token) throws RemoteException { + // TODO: confirm security token when we move timeout management into the HAL layer. + Slog.e(TAG, "Not supported"); + return; + } }; public FingerprintService(Context context) { super(context); + mTimedLockoutCleared = new SparseBooleanArray(); + mFailedAttempts = new SparseIntArray(); + mAlarmManager = context.getSystemService(AlarmManager.class); + context.registerReceiver(mLockoutReceiver, new IntentFilter(getLockoutResetIntent()), + getLockoutBroadcastPermission(), null /* handler */); } @Override @@ -767,18 +720,13 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { - return FingerprintUtils.getInstance(); - } - - @Override - protected int getFailedAttemptsLockoutTimed() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED; + protected DaemonWrapper getDaemonWrapper() { + return mDaemonWrapper; } @Override - protected int getFailedAttemptsLockoutPermanent() { - return MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT; + protected BiometricUtils getBiometricUtils() { + return FingerprintUtils.getInstance(); } @Override @@ -790,7 +738,7 @@ public class FingerprintService extends BiometricServiceBase { protected boolean hasReachedEnrollmentLimit(int userId) { final int limit = getContext().getResources().getInteger( com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser); - final int enrolled = FingerprintService.this.getEnrolledFingerprints(userId).size(); + final int enrolled = FingerprintService.this.getEnrolledTemplates(userId).size(); if (enrolled >= limit) { Slog.w(TAG, "Too many fingerprints registered"); return true; @@ -866,19 +814,6 @@ public class FingerprintService extends BiometricServiceBase { } @Override - protected void handleUserSwitching(int userId) { - if (getCurrentClient() instanceof InternalRemovalClient - || getCurrentClient() instanceof InternalEnumerateClient) { - Slog.w(TAG, "User switched while performing cleanup"); - removeClient(getCurrentClient()); - clearEnumerateState(); - } - updateActiveGroup(userId, null); - doFingerprintCleanupForUser(userId); - } - - - @Override protected boolean hasEnrolledBiometrics(int userId) { if (userId != UserHandle.getCallingUserId()) { checkPermission(INTERACT_ACROSS_USERS); @@ -913,6 +848,11 @@ public class FingerprintService extends BiometricServiceBase { } @Override + protected List<Fingerprint> getEnrolledTemplates(int userId) { + return getBiometricUtils().getBiometricsForUser(getContext(), userId); + } + + @Override protected void notifyClientActiveCallbacks(boolean isActive) { List<IFingerprintClientActiveCallback> callbacks = mClientActiveCallbacks; for (int i = 0; i < callbacks.size(); i++) { @@ -930,6 +870,20 @@ public class FingerprintService extends BiometricServiceBase { return BiometricsProtoEnums.MODALITY_FINGERPRINT; } + @Override + protected int getLockoutMode() { + final int currentUser = ActivityManager.getCurrentUser(); + final int failedAttempts = mFailedAttempts.get(currentUser, 0); + if (failedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) { + return AuthenticationClient.LOCKOUT_PERMANENT; + } else if (failedAttempts > 0 + && !mTimedLockoutCleared.get(currentUser, false) + && (failedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) { + return AuthenticationClient.LOCKOUT_TIMED; + } + return AuthenticationClient.LOCKOUT_NONE; + } + /** Gets the fingerprint daemon */ private synchronized IBiometricsFingerprint getFingerprintDaemon() { if (mDaemon == null) { @@ -959,7 +913,7 @@ public class FingerprintService extends BiometricServiceBase { if (mHalDeviceId != 0) { loadAuthenticatorIds(); updateActiveGroup(ActivityManager.getCurrentUser(), null); - doFingerprintCleanupForUser(ActivityManager.getCurrentUser()); + doTemplateCleanupForUser(ActivityManager.getCurrentUser()); } else { Slog.w(TAG, "Failed to open Fingerprint HAL!"); MetricsLogger.count(getContext(), "fingerprintd_openhal_error", 1); @@ -969,79 +923,6 @@ public class FingerprintService extends BiometricServiceBase { return mDaemon; } - /** - * This method should be called upon connection to the daemon, and when user switches. - * @param userId - */ - private void doFingerprintCleanupForUser(int userId) { - if (CLEANUP_UNUSED_FP) { - enumerateUser(userId); - } - } - - private void clearEnumerateState() { - if (DEBUG) Slog.v(TAG, "clearEnumerateState()"); - mUnknownFingerprints.clear(); - } - - private void enumerateUser(int userId) { - if (DEBUG) Slog.v(TAG, "Enumerating user(" + userId + ")"); - - final boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId); - - InternalEnumerateClient client = new InternalEnumerateClient(getContext(), mDaemonWrapper, - mHalDeviceId, mToken, new ServiceListenerImpl(null), userId, userId, restricted, - getContext().getOpPackageName(), enrolledList); - enumerateInternal(client); - } - - // Remove unknown fingerprints from hardware - private void cleanupUnknownFingerprints() { - if (!mUnknownFingerprints.isEmpty()) { - UserFingerprint uf = mUnknownFingerprints.get(0); - mUnknownFingerprints.remove(uf); - boolean restricted = !hasPermission(MANAGE_FINGERPRINT); - InternalRemovalClient client = new InternalRemovalClient(getContext(), mDaemonWrapper, - mHalDeviceId, mToken, new ServiceListenerImpl(null), uf.f.getBiometricId(), - uf.f.getGroupId(), uf.userId, restricted, getContext().getOpPackageName()); - removeInternal(client); - StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), - BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL); - } else { - clearEnumerateState(); - } - } - - private void handleEnumerate(Fingerprint fingerprint, int remaining) { - ClientMonitor client = getCurrentClient(); - - if ( !(client instanceof InternalRemovalClient) && !(client instanceof EnumerateClient) ) { - return; - } - client.onEnumerationResult(fingerprint, remaining); - - // All fingerprints in hardware for this user were enumerated - if (remaining == 0) { - if (client instanceof InternalEnumerateClient) { - List<Fingerprint> unknownFingerprints = - ((InternalEnumerateClient) client).getUnknownFingerprints(); - - if (!unknownFingerprints.isEmpty()) { - Slog.w(TAG, "Adding " + unknownFingerprints.size() + - " fingerprints for deletion"); - } - for (Fingerprint f : unknownFingerprints) { - mUnknownFingerprints.add(new UserFingerprint(f, client.getTargetUserId())); - } - removeClient(client); - cleanupUnknownFingerprints(); - } else { - removeClient(client); - } - } - } - private long startPreEnroll(IBinder token) { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { @@ -1070,8 +951,38 @@ public class FingerprintService extends BiometricServiceBase { return 0; } - private List<Fingerprint> getEnrolledFingerprints(int userId) { - return getBiometricUtils().getBiometricsForUser(getContext(), userId); + // Attempt counter should only be cleared when Keyguard goes away or when + // a biometric is successfully authenticated. Lockout should eventually be done below the HAL. + // See AuthenticationClient#shouldFrameworkHandleLockout(). + private void resetFailedAttemptsForUser(boolean clearAttemptCounter, int userId) { + if (DEBUG && getLockoutMode() != AuthenticationClient.LOCKOUT_NONE) { + Slog.v(getTag(), "Reset biometric lockout, clearAttemptCounter=" + clearAttemptCounter); + } + if (clearAttemptCounter) { + mFailedAttempts.put(userId, 0); + } + mTimedLockoutCleared.put(userId, true); + // If we're asked to reset failed attempts externally (i.e. from Keyguard), + // the alarm might still be pending; remove it. + cancelLockoutResetForUser(userId); + notifyLockoutResetMonitors(); + } + + + private void cancelLockoutResetForUser(int userId) { + mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); + } + + private void scheduleLockoutResetForUser(int userId) { + mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, + getLockoutResetIntentForUser(userId)); + } + + private PendingIntent getLockoutResetIntentForUser(int userId) { + return PendingIntent.getBroadcast(getContext(), userId, + new Intent(getLockoutResetIntent()).putExtra(KEY_LOCKOUT_RESET_USER, userId), + PendingIntent.FLAG_UPDATE_CURRENT); } private void dumpInternal(PrintWriter pw) { diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java index eb457b6bedf1..cb8a772356cc 100644 --- a/services/core/java/com/android/server/biometrics/iris/IrisService.java +++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java @@ -17,12 +17,16 @@ package com.android.server.biometrics.iris; import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; +import com.android.server.biometrics.AuthenticationClient; import com.android.server.biometrics.BiometricServiceBase; import com.android.server.biometrics.BiometricUtils; import com.android.server.biometrics.Metrics; +import java.util.List; + /** * A service to manage multiple clients that want to access the Iris HAL API. * The service is responsible for maintaining a list of clients and dispatching all @@ -61,18 +65,13 @@ public class IrisService extends BiometricServiceBase { } @Override - protected BiometricUtils getBiometricUtils() { + protected DaemonWrapper getDaemonWrapper() { return null; } @Override - protected int getFailedAttemptsLockoutTimed() { - return 0; - } - - @Override - protected int getFailedAttemptsLockoutPermanent() { - return 0; + protected BiometricUtils getBiometricUtils() { + return null; } @Override @@ -106,11 +105,6 @@ public class IrisService extends BiometricServiceBase { } @Override - protected void handleUserSwitching(int userId) { - - } - - @Override protected boolean hasEnrolledBiometrics(int userId) { return false; } @@ -131,7 +125,17 @@ public class IrisService extends BiometricServiceBase { } @Override + protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) { + return null; + } + + @Override protected int statsModality() { return BiometricsProtoEnums.MODALITY_IRIS; } + + @Override + protected int getLockoutMode() { + return AuthenticationClient.LOCKOUT_NONE; + } } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 19bdc0969e6d..c91e1a12078e 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1859,7 +1859,7 @@ public class Tethering extends BaseNetworkObserver { final TetherState tetherState = new TetherState( new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, makeControlCallback(), mConfig.enableLegacyDhcpServer, - mDeps.getIpServerDependencies(mContext))); + mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); tetherState.ipServer.start(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index e1af81b2f658..59fffb40fba7 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -241,7 +241,7 @@ public class Vpn { mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - updateCapabilities(); + updateCapabilities(null /* defaultNetwork */); loadAlwaysOnPackage(); } @@ -268,22 +268,44 @@ public class Vpn { updateAlwaysOnNotification(detailedState); } - public void updateCapabilities() { - final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null; - // Only apps targeting Q and above can explicitly declare themselves as metered. - final boolean isAlwaysMetered = - mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered); - updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks, - mNetworkCapabilities, isAlwaysMetered); + /** + * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a + * defensive copy. + * + * <p>Does not propagate updated capabilities to apps. + * + * @param defaultNetwork underlying network for VPNs following platform's default + */ + public synchronized NetworkCapabilities updateCapabilities( + @Nullable Network defaultNetwork) { + if (mConfig == null) { + // VPN is not running. + return null; + } - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + Network[] underlyingNetworks = mConfig.underlyingNetworks; + if (underlyingNetworks == null && defaultNetwork != null) { + // null underlying networks means to track the default. + underlyingNetworks = new Network[] { defaultNetwork }; } + // Only apps targeting Q and above can explicitly declare themselves as metered. + final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; + + applyUnderlyingCapabilities( + mContext.getSystemService(ConnectivityManager.class), + underlyingNetworks, + mNetworkCapabilities, + isAlwaysMetered); + + return new NetworkCapabilities(mNetworkCapabilities); } @VisibleForTesting - public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks, - NetworkCapabilities caps, boolean isAlwaysMetered) { + public static void applyUnderlyingCapabilities( + ConnectivityManager cm, + Network[] underlyingNetworks, + NetworkCapabilities caps, + boolean isAlwaysMetered) { int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; @@ -296,6 +318,7 @@ public class Vpn { boolean hadUnderlyingNetworks = false; if (null != underlyingNetworks) { for (Network underlying : underlyingNetworks) { + // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); if (underlyingCaps == null) continue; hadUnderlyingNetworks = true; @@ -895,6 +918,7 @@ public class Vpn { TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator); TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator); for (final RouteInfo route : routes) { + if (route.getType() == RouteInfo.RTN_UNREACHABLE) continue; IpPrefix destination = route.getDestination(); if (destination.isIPv4()) { ipv4Prefixes.add(destination); @@ -1007,9 +1031,8 @@ public class Vpn { } /** - * Establish a VPN network and return the file descriptor of the VPN - * interface. This methods returns {@code null} if the application is - * revoked or not prepared. + * Establish a VPN network and return the file descriptor of the VPN interface. This methods + * returns {@code null} if the application is revoked or not prepared. * * @param config The parameters to configure the network. * @return The file descriptor of the VPN interface. @@ -1101,8 +1124,6 @@ public class Vpn { // as rules are deleted. This prevents data leakage as the rules are moved over. agentDisconnect(oldNetworkAgent); } - // Set up VPN's capabilities such as meteredness. - updateCapabilities(); if (oldConnection != null) { mContext.unbindService(oldConnection); @@ -1258,6 +1279,11 @@ public class Vpn { return ranges; } + /** + * Updates UID ranges for this VPN and also updates its internal capabilities. + * + * <p>Should be called on primary ConnectivityService thread. + */ public void onUserAdded(int userHandle) { // If the user is restricted tie them to the parent user's VPN UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); @@ -1268,8 +1294,9 @@ public class Vpn { try { addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications, mConfig.disallowedApplications); + // ConnectivityService will call {@link #updateCapabilities} and apply + // those for VPN network. mNetworkCapabilities.setUids(existingRanges); - updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } @@ -1279,6 +1306,11 @@ public class Vpn { } } + /** + * Updates UID ranges for this VPN and also updates its capabilities. + * + * <p>Should be called on primary ConnectivityService thread. + */ public void onUserRemoved(int userHandle) { // clean up if restricted UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); @@ -1290,8 +1322,9 @@ public class Vpn { final List<UidRange> removedRanges = uidRangesForUser(userHandle, existingRanges); existingRanges.removeAll(removedRanges); + // ConnectivityService will call {@link #updateCapabilities} and + // apply those for VPN network. mNetworkCapabilities.setUids(existingRanges); - updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } @@ -1504,6 +1537,12 @@ public class Vpn { return success; } + /** + * Updates underlying network set. + * + * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from + * ConnectivityService thread to get updated capabilities. + */ public synchronized boolean setUnderlyingNetworks(Network[] networks) { if (!isCallerEstablishedOwnerLocked()) { return false; @@ -1520,7 +1559,6 @@ public class Vpn { } } } - updateCapabilities(); return true; } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index 3fddac111ec5..173d7860e4ac 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -61,8 +61,8 @@ public class TetheringDependencies { /** * Get dependencies to be used by IpServer. */ - public IpServer.Dependencies getIpServerDependencies(Context context) { - return new IpServer.Dependencies(context); + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies(); } /** diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java index 45f169ca0b6f..7dd3b363810d 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/ColorDisplayService.java @@ -129,240 +129,9 @@ public final class ColorDisplayService extends SystemService { private final NightDisplayTintController mNightDisplayTintController = new NightDisplayTintController(); - private final TintController mDisplayWhiteBalanceTintController = new TintController() { - // Three chromaticity coordinates per color: X, Y, and Z - private final int NUM_VALUES_PER_PRIMARY = 3; - // Four colors: red, green, blue, and white - private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; - - private final Object mLock = new Object(); - private int mTemperatureMin; - private int mTemperatureMax; - private int mTemperatureDefault; - private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - private ColorSpace.Rgb mDisplayColorSpaceRGB; - private float[] mChromaticAdaptationMatrix; - private int mCurrentColorTemperature; - private float[] mCurrentColorTemperatureXYZ; - private boolean mSetUp = false; - private float[] mMatrixDisplayWhiteBalance = new float[16]; - private Boolean mIsAvailable; - - @Override - public void setUp(Context context, boolean needsLinear) { - mSetUp = false; - final Resources res = context.getResources(); - - ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl(); - if (displayColorSpaceRGB == null) { - Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res"); - displayColorSpaceRGB = getDisplayColorSpaceFromResources(res); - if (displayColorSpaceRGB == null) { - Slog.e(TAG, "Failed to get display color space from resources"); - return; - } - } - - final String[] nominalWhiteValues = res.getStringArray( - R.array.config_displayWhiteBalanceDisplayNominalWhite); - float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - for (int i = 0; i < nominalWhiteValues.length; i++) { - displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); - } - - final int colorTemperatureMin = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureMin); - if (colorTemperatureMin <= 0) { - Slog.e(TAG, "Display white balance minimum temperature must be greater than 0"); - return; - } - - final int colorTemperatureMax = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureMax); - if (colorTemperatureMax < colorTemperatureMin) { - Slog.e(TAG, "Display white balance max temp must be greater or equal to min"); - return; - } - - final int colorTemperature = res.getInteger( - R.integer.config_displayWhiteBalanceColorTemperatureDefault); - - synchronized (mLock) { - mDisplayColorSpaceRGB = displayColorSpaceRGB; - mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; - mTemperatureMin = colorTemperatureMin; - mTemperatureMax = colorTemperatureMax; - mTemperatureDefault = colorTemperature; - mSetUp = true; - } - - setMatrix(mTemperatureDefault); - } - - @Override - public float[] getMatrix() { - return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; - } - - @Override - public void setMatrix(int cct) { - if (!mSetUp) { - Slog.w(TAG, "Can't set display white balance temperature: uninitialized"); - return; - } - - if (cct < mTemperatureMin) { - Slog.w(TAG, "Requested display color temperature is below allowed minimum"); - cct = mTemperatureMin; - } else if (cct > mTemperatureMax) { - Slog.w(TAG, "Requested display color temperature is above allowed maximum"); - cct = mTemperatureMax; - } - - Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct); - - synchronized (mLock) { - mCurrentColorTemperature = cct; - - // Adapt the display's nominal white point to match the requested CCT value - mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct); - - mChromaticAdaptationMatrix = - ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, - mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); - - // Convert the adaptation matrix to RGB space - float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, - mDisplayColorSpaceRGB.getTransform()); - result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); - - // Normalize the transform matrix to peak white value in RGB space - final float adaptedMaxR = result[0] + result[3] + result[6]; - final float adaptedMaxG = result[1] + result[4] + result[7]; - final float adaptedMaxB = result[2] + result[5] + result[8]; - final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); - for (int i = 0; i < result.length; i++) { - result[i] /= denum; - } - - Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); - java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); - java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); - java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); - } - } - - @Override - public int getLevel() { - return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; - } - - @Override - public boolean isAvailable(Context context) { - if (mIsAvailable == null) { - mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context); - } - return mIsAvailable; - } - - /** - * Format a given matrix into a string. - * - * @param matrix the matrix to format - * @param cols number of columns in the matrix - */ - private String matrixToString(float[] matrix, int cols) { - if (matrix == null || cols <= 0) { - Slog.e(TAG, "Invalid arguments when formatting matrix to string"); - return ""; - } - - StringBuilder sb = new StringBuilder(""); - for (int i = 0; i < matrix.length; i++) { - if (i % cols == 0) { - sb.append("\n "); - } - sb.append(String.format("%9.6f ", matrix[i])); - } - return sb.toString(); - } - - @Override - public void dump(PrintWriter pw) { - synchronized (mLock) { - pw.println(" mSetUp = " + mSetUp); - if (!mSetUp) { - return; - } - - pw.println(" isActivated = " + isActivated()); - pw.println(" mTemperatureMin = " + mTemperatureMin); - pw.println(" mTemperatureMax = " + mTemperatureMax); - pw.println(" mTemperatureDefault = " + mTemperatureDefault); - pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); - pw.println(" mCurrentColorTemperatureXYZ = " + - matrixToString(mCurrentColorTemperatureXYZ, 3)); - pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + - matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); - pw.println(" mChromaticAdaptationMatrix = " + - matrixToString(mChromaticAdaptationMatrix, 3)); - pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + - matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); - pw.println(" mMatrixDisplayWhiteBalance = " + - matrixToString(mMatrixDisplayWhiteBalance, 4)); - } - } - - private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) { - return new ColorSpace.Rgb( - "Display Color Space", - redGreenBlueXYZ, - whiteXYZ, - 2.2f // gamma, unused for display white balance - ); - } - - private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() { - final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); - if (displayToken == null) { - return null; - } - - DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken); - if (primaries == null || primaries.red == null || primaries.green == null || - primaries.blue == null || primaries.white == null) { - return null; - } - - return makeRgbColorSpaceFromXYZ( - new float[] { - primaries.red.X, primaries.red.Y, primaries.red.Z, - primaries.green.X, primaries.green.Y, primaries.green.Z, - primaries.blue.X, primaries.blue.Y, primaries.blue.Z, - }, - new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z } - ); - } - - private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) { - final String[] displayPrimariesValues = res.getStringArray( - R.array.config_displayWhiteBalanceDisplayPrimaries); - float[] displayRedGreenBlueXYZ = - new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY]; - float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; - - for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) { - displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]); - } - - for (int i = 0; i < displayWhiteXYZ.length; i++) { - displayWhiteXYZ[i] = Float.parseFloat( - displayPrimariesValues[displayRedGreenBlueXYZ.length + i]); - } - - return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); - } - }; + @VisibleForTesting + final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController = + new DisplayWhiteBalanceTintController(); private final TintController mGlobalSaturationTintController = new TintController() { @@ -860,7 +629,8 @@ public final class ColorDisplayService extends SystemService { return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt; } - private void updateDisplayWhiteBalanceStatus() { + @VisibleForTesting + void updateDisplayWhiteBalanceStatus() { boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() && !mNightDisplayTintController.isActivated() && @@ -1101,6 +871,7 @@ public final class ColorDisplayService extends SystemService { pw.println("Display white balance:"); if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { pw.println(" Activated: " + mDisplayWhiteBalanceTintController.isActivated()); + mDisplayWhiteBalanceTintController.dump(pw); } else { pw.println(" Not available"); } @@ -1533,6 +1304,244 @@ public final class ColorDisplayService extends SystemService { } } + final class DisplayWhiteBalanceTintController extends TintController { + // Three chromaticity coordinates per color: X, Y, and Z + private final int NUM_VALUES_PER_PRIMARY = 3; + // Four colors: red, green, blue, and white + private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; + + private final Object mLock = new Object(); + @VisibleForTesting + int mTemperatureMin; + @VisibleForTesting + int mTemperatureMax; + private int mTemperatureDefault; + private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + @VisibleForTesting + ColorSpace.Rgb mDisplayColorSpaceRGB; + private float[] mChromaticAdaptationMatrix; + @VisibleForTesting + int mCurrentColorTemperature; + private float[] mCurrentColorTemperatureXYZ; + private boolean mSetUp = false; + private float[] mMatrixDisplayWhiteBalance = new float[16]; + private Boolean mIsAvailable; + + @Override + public void setUp(Context context, boolean needsLinear) { + mSetUp = false; + final Resources res = context.getResources(); + + ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl(); + if (displayColorSpaceRGB == null) { + Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res"); + displayColorSpaceRGB = getDisplayColorSpaceFromResources(res); + if (displayColorSpaceRGB == null) { + Slog.e(TAG, "Failed to get display color space from resources"); + return; + } + } + + final String[] nominalWhiteValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayNominalWhite); + float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + for (int i = 0; i < nominalWhiteValues.length; i++) { + displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]); + } + + final int colorTemperatureMin = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMin); + if (colorTemperatureMin <= 0) { + Slog.e(TAG, "Display white balance minimum temperature must be greater than 0"); + return; + } + + final int colorTemperatureMax = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureMax); + if (colorTemperatureMax < colorTemperatureMin) { + Slog.e(TAG, "Display white balance max temp must be greater or equal to min"); + return; + } + + final int colorTemperature = res.getInteger( + R.integer.config_displayWhiteBalanceColorTemperatureDefault); + + synchronized (mLock) { + mDisplayColorSpaceRGB = displayColorSpaceRGB; + mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ; + mTemperatureMin = colorTemperatureMin; + mTemperatureMax = colorTemperatureMax; + mTemperatureDefault = colorTemperature; + mSetUp = true; + } + + setMatrix(mTemperatureDefault); + } + + @Override + public float[] getMatrix() { + return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY; + } + + @Override + public void setMatrix(int cct) { + if (!mSetUp) { + Slog.w(TAG, "Can't set display white balance temperature: uninitialized"); + return; + } + + if (cct < mTemperatureMin) { + Slog.w(TAG, "Requested display color temperature is below allowed minimum"); + cct = mTemperatureMin; + } else if (cct > mTemperatureMax) { + Slog.w(TAG, "Requested display color temperature is above allowed maximum"); + cct = mTemperatureMax; + } + + Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct); + + synchronized (mLock) { + mCurrentColorTemperature = cct; + + // Adapt the display's nominal white point to match the requested CCT value + mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct); + + mChromaticAdaptationMatrix = + ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD, + mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ); + + // Convert the adaptation matrix to RGB space + float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix, + mDisplayColorSpaceRGB.getTransform()); + result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result); + + // Normalize the transform matrix to peak white value in RGB space + final float adaptedMaxR = result[0] + result[3] + result[6]; + final float adaptedMaxG = result[1] + result[4] + result[7]; + final float adaptedMaxB = result[2] + result[5] + result[8]; + final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB); + for (int i = 0; i < result.length; i++) { + result[i] /= denum; + } + + Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0); + java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3); + java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3); + java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3); + } + } + + @Override + public int getLevel() { + return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; + } + + @Override + public boolean isAvailable(Context context) { + if (mIsAvailable == null) { + mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context); + } + return mIsAvailable; + } + + /** + * Format a given matrix into a string. + * + * @param matrix the matrix to format + * @param cols number of columns in the matrix + */ + private String matrixToString(float[] matrix, int cols) { + if (matrix == null || cols <= 0) { + Slog.e(TAG, "Invalid arguments when formatting matrix to string"); + return ""; + } + + StringBuilder sb = new StringBuilder(""); + for (int i = 0; i < matrix.length; i++) { + if (i % cols == 0) { + sb.append("\n "); + } + sb.append(String.format("%9.6f ", matrix[i])); + } + return sb.toString(); + } + + @Override + public void dump(PrintWriter pw) { + synchronized (mLock) { + pw.println(" mSetUp = " + mSetUp); + if (!mSetUp) { + return; + } + + pw.println(" mTemperatureMin = " + mTemperatureMin); + pw.println(" mTemperatureMax = " + mTemperatureMax); + pw.println(" mTemperatureDefault = " + mTemperatureDefault); + pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); + pw.println(" mCurrentColorTemperatureXYZ = " + + matrixToString(mCurrentColorTemperatureXYZ, 3)); + pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); + pw.println(" mChromaticAdaptationMatrix = " + + matrixToString(mChromaticAdaptationMatrix, 3)); + pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); + pw.println(" mMatrixDisplayWhiteBalance = " + + matrixToString(mMatrixDisplayWhiteBalance, 4)); + } + } + + private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) { + return new ColorSpace.Rgb( + "Display Color Space", + redGreenBlueXYZ, + whiteXYZ, + 2.2f // gamma, unused for display white balance + ); + } + + private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() { + final IBinder displayToken = SurfaceControl.getInternalDisplayToken(); + if (displayToken == null) { + return null; + } + + DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken); + if (primaries == null || primaries.red == null || primaries.green == null || + primaries.blue == null || primaries.white == null) { + return null; + } + + return makeRgbColorSpaceFromXYZ( + new float[] { + primaries.red.X, primaries.red.Y, primaries.red.Z, + primaries.green.X, primaries.green.Y, primaries.green.Z, + primaries.blue.X, primaries.blue.Y, primaries.blue.Z, + }, + new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z } + ); + } + + private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) { + final String[] displayPrimariesValues = res.getStringArray( + R.array.config_displayWhiteBalanceDisplayPrimaries); + float[] displayRedGreenBlueXYZ = + new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY]; + float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY]; + + for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) { + displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]); + } + + for (int i = 0; i < displayWhiteXYZ.length; i++) { + displayWhiteXYZ[i] = Float.parseFloat( + displayPrimariesValues[displayRedGreenBlueXYZ.length + i]); + } + + return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); + } + }; + /** * Local service that allows color transforms to be enabled from other system services. */ diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 6ee5665b9e42..e9ae516cc8d0 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; +import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; @@ -225,8 +226,12 @@ abstract class DisplayDevice { viewport.deviceHeight = isRotated ? info.width : info.height; viewport.uniqueId = info.uniqueId; - // TODO(b/112898898) Use an actual port here. - viewport.physicalPort = null; + + if (info.address instanceof DisplayAddress.Physical) { + viewport.physicalPort = ((DisplayAddress.Physical) info.address).getPort(); + } else { + viewport.physicalPort = null; + } } /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index ab64f61a3b22..729ea1772066 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.hardware.display.DisplayViewport; import android.util.DisplayMetrics; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.Surface; @@ -274,7 +275,7 @@ final class DisplayDeviceInfo { * Display address, or null if none. * Interpretation varies by display type. */ - public String address; + public DisplayAddress address; /** * Display state. diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index dc5be6a6a074..15c7ef75866f 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1019,7 +1019,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mDisplayWhiteBalanceController != null) { if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) { mDisplayWhiteBalanceController.setEnabled(true); - mDisplayWhiteBalanceController.updateScreenColorTemperature(); + mDisplayWhiteBalanceController.updateDisplayColorTemperature(); } else { mDisplayWhiteBalanceController.setEnabled(false); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 28f21f633ac4..489194726c5a 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -31,6 +31,7 @@ import android.util.LongSparseArray; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayAddress; import android.view.DisplayCutout; import android.view.DisplayEventReceiver; import android.view.Surface; @@ -382,6 +383,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.presentationDeadlineNanos = phys.presentationDeadlineNanos; mInfo.state = mState; mInfo.uniqueId = getUniqueId(); + mInfo.address = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); // Assume that all built-in displays that have secure output (eg. HDCP) also // support compositing from gralloc protected buffers. diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index e65637f04975..2f507d17730e 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -308,7 +308,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; } mInfo.type = Display.TYPE_OVERLAY; - mInfo.touch = DisplayDeviceInfo.TOUCH_NONE; + mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL; mInfo.state = mState; } return mInfo; diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java index e8d6ad455fbf..9e4c1cb57dca 100644 --- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java @@ -16,9 +16,6 @@ package com.android.server.display; -import com.android.internal.util.DumpUtils; -import com.android.internal.util.IndentingPrintWriter; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -35,9 +32,13 @@ import android.os.Message; import android.os.UserHandle; import android.util.Slog; import android.view.Display; +import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -581,7 +582,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { private final int mHeight; private final float mRefreshRate; private final int mFlags; - private final String mAddress; + private final DisplayAddress mAddress; private final Display.Mode mMode; private Surface mSurface; @@ -596,7 +597,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { mHeight = height; mRefreshRate = refreshRate; mFlags = flags; - mAddress = address; + mAddress = DisplayAddress.fromMacAddress(address); mSurface = surface; mMode = createMode(width, height, refreshRate); } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index b9aa34e89216..d95e92b71357 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -29,14 +29,14 @@ import java.io.PrintWriter; /** * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the - * screen color temperature depending on the ambient color temperature). + * display color temperature depending on the ambient color temperature). * * The DisplayWhiteBalanceController: * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature; * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the * noise, and arrive at an estimate of the actual ambient color temperature; - * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be - * updated, suppressing changes that are too frequent or too minor. + * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should + * be updated, suppressing changes that are too frequent or too minor. */ public class DisplayWhiteBalanceController implements AmbientSensor.AmbientBrightnessSensor.Callbacks, @@ -76,8 +76,8 @@ public class DisplayWhiteBalanceController implements // Override the ambient color temperature for debugging purposes. private float mAmbientColorTemperatureOverride; - // A piecewise linear relationship between ambient and display color temperatures - private Spline.LinearSpline mAmbientToDisplayTemperatureSpline; + // A piecewise linear relationship between ambient and display color temperatures. + private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline; /** * @param brightnessSensor @@ -91,7 +91,7 @@ public class DisplayWhiteBalanceController implements * The filter used to average ambient color temperature changes over time, filter out the * noise and arrive at an estimate of the actual ambient color temperature. * @param throttler - * The throttler used to determine whether the new screen color temperature should be + * The throttler used to determine whether the new display color temperature should be * updated or not. * @param lowLightAmbientBrightnessThreshold * The ambient brightness threshold beneath which we fall back to a fixed ambient color @@ -99,6 +99,12 @@ public class DisplayWhiteBalanceController implements * @param lowLightAmbientColorTemperature * The ambient color temperature to which we fall back when the ambient brightness drops * beneath a certain threshold. + * @param ambientColorTemperatures + * The ambient color tempeartures used to map the ambient color temperature to the display + * color temperature (or null if no mapping is necessary). + * @param displayColorTemperatures + * The display color temperatures used to map the ambient color temperature to the display + * color temperature (or null if no mapping is necessary). * * @throws NullPointerException * - brightnessSensor is null; @@ -114,7 +120,7 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature, - float[] ambientTemperatures, float[] displayTemperatures) { + float[] ambientColorTemperatures, float[] displayColorTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); mLoggingEnabled = false; @@ -134,10 +140,10 @@ public class DisplayWhiteBalanceController implements mAmbientColorTemperatureOverride = -1.0f; try { - mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures, - displayTemperatures); + mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline( + ambientColorTemperatures, displayColorTemperatures); } catch (Exception e) { - mAmbientToDisplayTemperatureSpline = null; + mAmbientToDisplayColorTemperatureSpline = null; } mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class); @@ -160,7 +166,7 @@ public class DisplayWhiteBalanceController implements } /** - * Set an object to call back to when the screen color temperature should be updated. + * Set an object to call back to when the display color temperature should be updated. * * @param callbacks * The object to call back to. @@ -201,7 +207,7 @@ public class DisplayWhiteBalanceController implements * * This is only applied when the ambient color temperature changes or is updated (in which case * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily - * change the screen color temperature immediately. + * change the display color temperature immediately. * * @param ambientColorTemperatureOverride * The ambient color temperature override. @@ -240,9 +246,8 @@ public class DisplayWhiteBalanceController implements writer.println(" mLastAmbientColorTemperature=" + mLastAmbientColorTemperature); writer.println(" mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory); writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); - writer.println(" mAmbientToDisplayTemperatureSpline=" - + (mAmbientToDisplayTemperatureSpline == null ? "unused" : - mAmbientToDisplayTemperatureSpline)); + writer.println(" mAmbientToDisplayColorTemperatureSpline=" + + mAmbientToDisplayColorTemperatureSpline); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -266,9 +271,9 @@ public class DisplayWhiteBalanceController implements final long time = System.currentTimeMillis(); float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time); - if (mAmbientToDisplayTemperatureSpline != null) { + if (mAmbientToDisplayColorTemperatureSpline != null) { ambientColorTemperature = - mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature); + mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature); } final float ambientBrightness = mBrightnessFilter.getEstimate(time); @@ -290,7 +295,7 @@ public class DisplayWhiteBalanceController implements ambientColorTemperature = mAmbientColorTemperatureOverride; } - // When the screen color temperature needs to be updated, we call DisplayPowerController to + // When the display color temperature needs to be updated, we call DisplayPowerController to // call our updateColorTemperature. The reason we don't call it directly is that we want // all changes to the system to happen in a predictable order in DPC's main loop // (updatePowerState). @@ -308,9 +313,9 @@ public class DisplayWhiteBalanceController implements } /** - * Updates the screen color temperature. + * Updates the display color temperature. */ - public void updateScreenColorTemperature() { + public void updateDisplayColorTemperature() { float ambientColorTemperature = -1.0f; // If both the pending and the current ambient color temperatures are -1, it means the DWBC @@ -353,7 +358,7 @@ public class DisplayWhiteBalanceController implements * Called whenever the display white-balance state has changed. * * Usually, this means the estimated ambient color temperature has changed enough, and the - * screen color temperature should be updated; but it is also called by + * display color temperature should be updated; but it is also called if settings change. */ void updateWhiteBalance(); } diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index 56f4ca339eb3..449f115ad3e3 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -67,14 +67,14 @@ public class DisplayWhiteBalanceFactory { final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); - final float[] ambientTemperatures = getFloatArray(resources, - com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues); - final float[] displayTemperatures = getFloatArray(resources, - com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues); + final float[] ambientColorTemperatures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures); + final float[] displayColorTempeartures = getFloatArray(resources, + com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature, - ambientTemperatures, displayTemperatures); + ambientColorTemperatures, displayColorTempeartures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); return controller; diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java index c1f0e983a366..5941bbb62d94 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java @@ -23,7 +23,7 @@ import java.util.Arrays; /** * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the - * screen color temperature should be updated, suppressing changes that are too frequent or too + * display color temperature should be updated, suppressing changes that are too frequent or too * minor. */ class DisplayWhiteBalanceThrottler { diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index a68ceed750ad..6899c3ffcbb1 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -22,24 +22,33 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import android.annotation.NonNull; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.gamedriver.GameDriverProto.Blacklist; +import android.gamedriver.GameDriverProto.Blacklists; import android.net.Uri; import android.os.Build; +import android.os.Handler; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; +import android.util.Base64; import android.util.Slog; +import com.android.framework.protobuf.InvalidProtocolBufferException; +import com.android.internal.annotations.GuardedBy; import com.android.server.SystemService; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; /** * Service to manage GPU related features. @@ -52,17 +61,25 @@ public class GpuService extends SystemService { public static final boolean DEBUG = false; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; + private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; private final Context mContext; private final String mDriverPackageName; private final PackageManager mPackageManager; + private final Object mLock = new Object(); + private ContentResolver mContentResolver; + private long mGameDriverVersionCode; + private SettingsObserver mSettingsObserver; + @GuardedBy("mLock") + private Blacklists mBlacklists; public GpuService(Context context) { super(context); mContext = context; mDriverPackageName = SystemProperties.get(PROPERTY_GFX_DRIVER); + mGameDriverVersionCode = -1; mPackageManager = context.getPackageManager(); if (mDriverPackageName != null && !mDriverPackageName.isEmpty()) { final IntentFilter packageFilter = new IntentFilter(); @@ -82,10 +99,37 @@ public class GpuService extends SystemService { @Override public void onBootPhase(int phase) { if (phase == PHASE_BOOT_COMPLETED) { + mContentResolver = mContext.getContentResolver(); + mSettingsObserver = new SettingsObserver(); if (mDriverPackageName == null || mDriverPackageName.isEmpty()) { return; } fetchGameDriverPackageProperties(); + processBlacklists(); + setBlacklist(); + } + } + + private final class SettingsObserver extends ContentObserver { + private final Uri mGameDriverBlackUri = + Settings.Global.getUriFor(Settings.Global.GAME_DRIVER_BLACKLISTS); + + SettingsObserver() { + super(new Handler()); + mContentResolver.registerContentObserver(mGameDriverBlackUri, false, this, + UserHandle.USER_ALL); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri == null) { + return; + } + + if (mGameDriverBlackUri.equals(uri)) { + processBlacklists(); + setBlacklist(); + } } } @@ -109,6 +153,7 @@ public class GpuService extends SystemService { case ACTION_PACKAGE_CHANGED: case ACTION_PACKAGE_REMOVED: fetchGameDriverPackageProperties(); + setBlacklist(); break; default: // do nothing @@ -138,16 +183,22 @@ public class GpuService extends SystemService { return; } + // Reset the whitelist. + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_WHITELIST, ""); + mGameDriverVersionCode = driverInfo.longVersionCode; + try { final Context driverContext = mContext.createPackageContext(mDriverPackageName, Context.CONTEXT_RESTRICTED); final BufferedReader reader = new BufferedReader( - new InputStreamReader(driverContext.getAssets().open(WHITELIST_FILENAME))); + new InputStreamReader(driverContext.getAssets() + .open(GAME_DRIVER_WHITELIST_FILENAME))); final ArrayList<String> whitelistedPackageNames = new ArrayList<>(); for (String packageName; (packageName = reader.readLine()) != null; ) { whitelistedPackageNames.add(packageName); } - Settings.Global.putString(mContext.getContentResolver(), + Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_WHITELIST, String.join(",", whitelistedPackageNames)); } catch (PackageManager.NameNotFoundException e) { @@ -160,4 +211,48 @@ public class GpuService extends SystemService { } } } + + private void processBlacklists() { + // TODO(b/121350991) Switch to DeviceConfig with property listener. + String base64String = + Settings.Global.getString(mContentResolver, Settings.Global.GAME_DRIVER_BLACKLISTS); + if (base64String == null || base64String.isEmpty()) { + return; + } + + synchronized (mLock) { + // Reset all blacklists + mBlacklists = null; + try { + mBlacklists = Blacklists.parseFrom(Base64.decode(base64String, BASE64_FLAGS)); + } catch (IllegalArgumentException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } catch (InvalidProtocolBufferException e) { + if (DEBUG) { + Slog.w(TAG, "Can't parse blacklist, skip and continue..."); + } + } + } + } + + private void setBlacklist() { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, ""); + synchronized (mLock) { + if (mBlacklists == null) { + return; + } + List<Blacklist> blacklists = mBlacklists.getBlacklistsList(); + for (Blacklist blacklist : blacklists) { + if (blacklist.getVersionCode() == mGameDriverVersionCode) { + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_BLACKLIST, + String.join(",", blacklist.getPackageNamesList())); + return; + } + } + } + } } diff --git a/services/core/java/com/android/server/input/InputForwarder.java b/services/core/java/com/android/server/input/InputForwarder.java deleted file mode 100644 index 00af8398d0ff..000000000000 --- a/services/core/java/com/android/server/input/InputForwarder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.input; - -import android.app.IInputForwarder; -import android.hardware.input.InputManagerInternal; -import android.view.InputEvent; - -import com.android.server.LocalServices; - -import static android.hardware.input.InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; - -/** - * Basic implementation of {@link IInputForwarder}. - */ -class InputForwarder extends IInputForwarder.Stub { - - private final InputManagerInternal mInputManagerInternal; - private final int mDisplayId; - - InputForwarder(int displayId) { - mDisplayId = displayId; - mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); - } - - @Override - public boolean forwardEvent(InputEvent event) { - event.setDisplayId(mDisplayId); - return mInputManagerInternal.injectInputEvent(event, INJECT_INPUT_EVENT_MODE_ASYNC); - } -}
\ No newline at end of file diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 28393a209111..87c744138797 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -17,7 +17,6 @@ package com.android.server.input; import android.annotation.NonNull; -import android.app.IInputForwarder; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -1685,29 +1684,6 @@ public class InputManagerService extends IInputManager.Stub nativeMonitor(mPtr); } - // Binder call - @Override - public IInputForwarder createInputForwarder(int displayId) throws RemoteException { - if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS, - "createInputForwarder()")) { - throw new SecurityException("Requires INJECT_EVENTS permission"); - } - final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); - final Display display = displayManager.getDisplay(displayId); - if (display == null) { - throw new IllegalArgumentException( - "Can't create input forwarder for non-existent displayId: " + displayId); - } - final int callingUid = Binder.getCallingUid(); - final int displayOwnerUid = display.getOwnerUid(); - if (callingUid != displayOwnerUid) { - throw new SecurityException( - "Only owner of the display can forward input events to it."); - } - - return new InputForwarder(displayId); - } - // Native callback. private void notifyConfigurationChanged(long whenNanos) { mWindowManagerCallbacks.notifyConfigurationChanged(); diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 3abacc2c9c10..243b6fe7a30e 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -373,6 +373,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private final NtpTimeHelper mNtpTimeHelper; private final GnssBatchingProvider mGnssBatchingProvider; private final GnssGeofenceProvider mGnssGeofenceProvider; + // Available only on GNSS HAL 2.0 implementations and later. private GnssVisibilityControl mGnssVisibilityControl; // Handler for processing events @@ -463,8 +464,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } }; - // TODO(b/119326010): replace OnSubscriptionsChangedListener with - // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver. + // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED + // broadcast receiver. private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = new OnSubscriptionsChangedListener() { @Override @@ -676,8 +677,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mNtpTimeHelper.onNetworkAvailable(); if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) { if (mSupportsXtra) { - // Download only if supported, (prevents an unneccesary on-boot - // download) + // Download only if supported, (prevents an unnecessary on-boot download) xtraDownloadRequest(); } } @@ -764,7 +764,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements /** Returns true if the location request is too frequent. */ private boolean isRequestLocationRateLimited() { - // TODO(b/73198123): implement exponential backoff. + // TODO: implement exponential backoff. return false; } @@ -917,7 +917,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements synchronized (mLock) { boolean enabled = ((mProviderRequest != null && mProviderRequest.reportLocation - && mProviderRequest.forceLocation) || ( + && mProviderRequest.locationSettingsIgnored) || ( mContext.getSystemService(LocationManager.class).isLocationEnabled() && !mDisableGps)) && !mShutdown; if (enabled == mEnabled) { diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java index c3f25bfa2e5e..20f872a213fd 100644 --- a/services/core/java/com/android/server/location/GnssVisibilityControl.java +++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java @@ -24,10 +24,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.database.ContentObserver; +import android.location.LocationManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; +import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.util.StatsLog; @@ -70,7 +73,7 @@ class GnssVisibilityControl { private final Handler mHandler; private final Context mContext; - private boolean mIsMasterLocationSettingsEnabled = true; + private boolean mIsDeviceLocationSettingsEnabled; // Number of non-framework location access proxy apps is expected to be small (< 5). private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7; @@ -88,13 +91,9 @@ class GnssVisibilityControl { mAppOps = mContext.getSystemService(AppOpsManager.class); mPackageManager = mContext.getPackageManager(); - // Set to empty proxy app list initially until the configuration properties are loaded. - updateNfwLocationAccessProxyAppsInGnssHal(); - - // Listen for proxy app package installation, removal events. - listenForProxyAppsPackageUpdates(); - - // TODO(b/122855984): Handle global location settings on/off. + // Complete initialization as the first event to run in mHandler thread. After that, + // all object state read/update events run in the mHandler thread. + runOnHandler(this::handleInitialize); } void updateProxyApps(List<String> nfwLocationAccessProxyApps) { @@ -105,10 +104,6 @@ class GnssVisibilityControl { runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps)); } - void masterLocationSettingsUpdated(boolean enabled) { - runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled)); - } - void reportNfwNotification(String proxyAppPackageName, byte protocolStack, String otherProtocolStackName, byte requestor, String requestorId, byte responseType, boolean inEmergencyMode, boolean isCachedLocation) { @@ -117,7 +112,19 @@ class GnssVisibilityControl { requestor, requestorId, responseType, inEmergencyMode, isCachedLocation))); } + private void handleInitialize() { + disableNfwLocationAccess(); // Disable until config properties are loaded. + listenForProxyAppsPackageUpdates(); + listenForDeviceLocationSettingsUpdate(); + mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings(); + } + + private boolean getDeviceLocationSettings() { + return mContext.getSystemService(LocationManager.class).isLocationEnabled(); + } + private void listenForProxyAppsPackageUpdates() { + // Listen for proxy apps package installation, removal events. IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -143,11 +150,22 @@ class GnssVisibilityControl { }, UserHandle.ALL, intentFilter, null, mHandler); } + private void listenForDeviceLocationSettingsUpdate() { + mContext.getContentResolver().registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), + true, + new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + handleDeviceLocationSettingsUpdated(); + } + }, UserHandle.USER_ALL); + } + private void handleProxyAppPackageUpdate(String pkgName, String action) { final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName); - // pkgName is not one of the proxy apps in our list. if (locationPermission == null) { - return; + return; // ignore, pkgName is not one of the proxy apps in our list. } Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action); @@ -197,15 +215,33 @@ class GnssVisibilityControl { return true; } } - return false; } - private void handleMasterLocationSettingsUpdated(boolean enabled) { - mIsMasterLocationSettingsEnabled = enabled; - Log.i(TAG, "Master location settings switch changed to " - + (enabled ? "enabled" : "disabled")); - updateNfwLocationAccessProxyAppsInGnssHal(); + private void handleDeviceLocationSettingsUpdated() { + final boolean enabled = getDeviceLocationSettings(); + Log.i(TAG, "Device location settings enabled: " + enabled); + + if (mIsDeviceLocationSettingsEnabled == enabled) { + return; + } + + mIsDeviceLocationSettingsEnabled = enabled; + if (!mIsDeviceLocationSettingsEnabled) { + disableNfwLocationAccess(); + return; + } + + // When device location settings was disabled, we already set the proxy app list + // to empty in GNSS HAL. Update only if the proxy app list is not empty. + String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps(); + if (locationPermissionEnabledProxyApps.length != 0) { + setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps); + } + } + + private void disableNfwLocationAccess() { + setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS); } // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal @@ -316,8 +352,7 @@ class GnssVisibilityControl { return mPackageManager.getApplicationInfo(pkgName, 0).uid; } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { - Log.d(TAG, "Non-framework location access proxy app " - + pkgName + " is not found."); + Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found."); } return null; } @@ -329,8 +364,14 @@ class GnssVisibilityControl { } private void updateNfwLocationAccessProxyAppsInGnssHal() { - final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess() - ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps(); + if (!mIsDeviceLocationSettingsEnabled) { + return; // Keep non-framework location access disabled. + } + setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps()); + } + + private void setNfwLocationAccessProxyAppsInGnssHal( + String[] locationPermissionEnabledProxyApps) { final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps); Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: " + proxyAppsStr); @@ -341,12 +382,8 @@ class GnssVisibilityControl { } } - private boolean shouldDisableNfwLocationAccess() { - return !mIsMasterLocationSettingsEnabled; - } - private String[] getLocationPermissionEnabledProxyApps() { - // Get a count of proxy apps with location permission enabled to array creation size. + // Get a count of proxy apps with location permission enabled for array creation size. int countLocationPermissionEnabledProxyApps = 0; for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) { if (hasLocationPermissionEnabled) { diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a9ae74f67de7..4b4788cd4a16 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -55,6 +55,8 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.database.sqlite.SQLiteDatabase; import android.hardware.authsecret.V1_0.IAuthSecret; +import android.hardware.biometrics.BiometricManager; +import android.hardware.face.FaceManager; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -671,7 +673,6 @@ public class LockSettingsService extends ILockSettings.Stub { mDeviceProvisionedObserver.onSystemReady(); // TODO: maybe skip this for split system user mode. mStorage.prefetchUser(UserHandle.USER_SYSTEM); - mStrongAuth.systemReady(); } private void migrateOldData() { @@ -2375,6 +2376,14 @@ public class LockSettingsService extends ILockSettings.Stub { userCredential = null; } + final PackageManager pm = mContext.getPackageManager(); + // TODO: When lockout is handled under the HAL for all biometrics (fingerprint), + // we need to generate challenge for each one, have it signed by GK and reset lockout + // for each modality. + if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + challenge = mContext.getSystemService(FaceManager.class).generateChallenge(); + } + final AuthenticationResult authResult; VerifyCredentialResponse response; synchronized (mSpManager) { @@ -2413,6 +2422,17 @@ public class LockSettingsService extends ILockSettings.Stub { if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { notifyActivePasswordMetricsAvailable(userCredential, userId); unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId); + // Reset lockout + if (BiometricManager.hasBiometrics(mContext)) { + BiometricManager bm = mContext.getSystemService(BiometricManager.class); + Slog.i(TAG, "Resetting lockout, length: " + + authResult.gkResponse.getPayload().length); + bm.resetLockout(authResult.gkResponse.getPayload()); + + if (!hasChallenge && pm.hasSystemFeature(PackageManager.FEATURE_FACE)) { + mContext.getSystemService(FaceManager.class).revokeChallenge(); + } + } final byte[] secret = authResult.authToken.deriveDiskEncryptionKey(); Slog.i(TAG, "Unlocking user " + userId + " with secret only, length " + secret.length); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java index 44804350309d..a84306c97dc0 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java @@ -16,15 +16,16 @@ package com.android.server.locksettings; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED; -import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_NOT_REQUIRED; +import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker + .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.content.Context; -import android.hardware.biometrics.BiometricManager; import android.os.Handler; import android.os.Message; import android.os.RemoteCallbackList; @@ -61,7 +62,6 @@ public class LockSettingsStrongAuth { private final Context mContext; private AlarmManager mAlarmManager; - private BiometricManager mBiometricManager; public LockSettingsStrongAuth(Context context) { mContext = context; @@ -69,12 +69,6 @@ public class LockSettingsStrongAuth { mAlarmManager = context.getSystemService(AlarmManager.class); } - public void systemReady() { - if (BiometricManager.hasBiometrics(mContext)) { - mBiometricManager = mContext.getSystemService(BiometricManager.class); - } - } - private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) { mTrackers.register(tracker); @@ -185,11 +179,6 @@ public class LockSettingsStrongAuth { } public void reportSuccessfulStrongAuthUnlock(int userId) { - if (mBiometricManager != null) { - byte[] token = null; /* TODO: pass real auth token once HAL supports it */ - mBiometricManager.resetTimeout(token); - } - final int argNotUsed = 0; mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget(); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java index c62a31e24fb2..ff22a8dc934f 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertXml.java @@ -20,6 +20,8 @@ import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; +import org.w3c.dom.Element; + import java.security.SecureRandom; import java.security.cert.CertPath; import java.security.cert.X509Certificate; @@ -28,8 +30,6 @@ import java.util.Collections; import java.util.Date; import java.util.List; -import org.w3c.dom.Element; - /** * Parses and holds the XML file containing the list of THM public-key certificates and related * metadata. @@ -38,24 +38,20 @@ public final class CertXml { private static final String METADATA_NODE_TAG = "metadata"; private static final String METADATA_SERIAL_NODE_TAG = "serial"; - private static final String METADATA_REFRESH_INTERVAL_NODE_TAG = "refresh-interval"; private static final String ENDPOINT_CERT_LIST_TAG = "endpoints"; private static final String ENDPOINT_CERT_ITEM_TAG = "cert"; private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates"; private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert"; private final long serial; - private final long refreshInterval; private final List<X509Certificate> intermediateCerts; private final List<X509Certificate> endpointCerts; private CertXml( long serial, - long refreshInterval, List<X509Certificate> intermediateCerts, List<X509Certificate> endpointCerts) { this.serial = serial; - this.refreshInterval = refreshInterval; this.intermediateCerts = intermediateCerts; this.endpointCerts = endpointCerts; } @@ -65,15 +61,6 @@ public final class CertXml { return serial; } - /** - * Gets the refresh interval in the XML file containing public-key certificates. The refresh - * interval denotes the number of seconds that the client should follow to contact the server to - * refresh the XML file. - */ - public long getRefreshInterval() { - return refreshInterval; - } - @VisibleForTesting List<X509Certificate> getAllIntermediateCerts() { return intermediateCerts; @@ -121,7 +108,6 @@ public final class CertXml { Element rootNode = CertUtils.getXmlRootNode(bytes); return new CertXml( parseSerial(rootNode), - parseRefreshInterval(rootNode), parseIntermediateCerts(rootNode), parseEndpointCerts(rootNode)); } @@ -136,16 +122,6 @@ public final class CertXml { return Long.parseLong(contents.get(0)); } - private static long parseRefreshInterval(Element rootNode) throws CertParsingException { - List<String> contents = - CertUtils.getXmlNodeContents( - CertUtils.MUST_EXIST_EXACTLY_ONE, - rootNode, - METADATA_NODE_TAG, - METADATA_REFRESH_INTERVAL_NODE_TAG); - return Long.parseLong(contents.get(0)); - } - private static List<X509Certificate> parseIntermediateCerts(Element rootNode) throws CertParsingException { List<String> contents = diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b6ef180f4b59..b221241c25e2 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -1207,6 +1207,15 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } } + public void setPlaybackSpeed(String packageName, int pid, int uid, + ControllerCallbackLink caller, float speed) { + try { + mCb.notifySetPlaybackSpeed(packageName, pid, uid, caller, speed); + } catch (RuntimeException e) { + Slog.e(TAG, "Remote failure in setPlaybackSpeed.", e); + } + } + public void adjustVolume(String packageName, int pid, int uid, ControllerCallbackLink caller, boolean asSystemService, int direction) { try { @@ -1446,6 +1455,13 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public void setPlaybackSpeed(String packageName, ControllerCallbackLink caller, + float speed) { + mSessionCb.setPlaybackSpeed(packageName, Binder.getCallingPid(), Binder.getCallingUid(), + caller, speed); + } + + @Override public void sendCustomAction(String packageName, ControllerCallbackLink caller, String action, Bundle args) { mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(), diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index 9e5b92a6b944..3f15b381c18b 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -17,9 +17,6 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.provider.Settings.ACTION_VPN_SETTINGS; import android.app.Notification; @@ -30,17 +27,14 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; -import android.net.LinkProperties; import android.net.LinkAddress; +import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkInfo.State; -import android.net.NetworkPolicyManager; import android.os.INetworkManagementService; -import android.os.RemoteException; import android.security.Credentials; import android.security.KeyStore; -import android.system.Os; import android.text.TextUtils; import android.util.Slog; diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 4bd8f450c76b..6d82c1c257ab 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -15,15 +15,15 @@ */ package com.android.server.net; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -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 android.app.ActivityManager; import android.net.NetworkPolicyManager; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index af55605975ca..75b62cb349af 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -38,6 +38,11 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLE import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -45,12 +50,7 @@ import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; 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_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -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.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index e479a1539e25..d0c59c1b002e 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -320,6 +320,11 @@ public final class OverlayManagerService extends SystemService { private final class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { + final String action = intent.getAction(); + if (action == null) { + Slog.e(TAG, "Cannot handle package broadcast with null action"); + return; + } final Uri data = intent.getData(); if (data == null) { Slog.e(TAG, "Cannot handle package broadcast with null data"); @@ -337,7 +342,7 @@ public final class OverlayManagerService extends SystemService { userIds = new int[] { UserHandle.getUserId(extraUid) }; } - switch (intent.getAction()) { + switch (action) { case ACTION_PACKAGE_ADDED: if (replacing) { onPackageUpgraded(packageName, userIds); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index dac4b6ff39c3..8051abbdd417 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -27,6 +27,7 @@ import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; @@ -50,8 +51,12 @@ class ApexManager { private final Map<String, PackageInfo> mActivePackagesCache; ApexManager() { - mApexService = IApexService.Stub.asInterface( - ServiceManager.getService("apexservice")); + try { + mApexService = IApexService.Stub.asInterface( + ServiceManager.getServiceOrThrow("apexservice")); + } catch (ServiceNotFoundException e) { + throw new IllegalStateException("Required service apexservice not available"); + } mActivePackagesCache = populateActivePackagesCache(); } @@ -151,7 +156,7 @@ class ApexManager { } /** - * Mark a staged session previously submitted using {@cde submitStagedSession} as ready to be + * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be * applied at next reboot. * * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready. @@ -167,6 +172,27 @@ class ApexManager { } /** + * Marks a staged session as successful. + * + * <p>Only activated session can be marked as successful. + * + * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as + * successful. + */ + void markStagedSessionSuccessful(int sessionId) { + try { + mApexService.markStagedSessionSuccessful(sessionId); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + throw new RuntimeException(re); + } catch (Exception e) { + // It is fine to just log an exception in this case. APEXd will be able to recover in + // case markStagedSessionSuccessful fails. + Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e); + } + } + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -196,7 +222,7 @@ class ApexManager { ipw.increaseIndent(); final ApexSessionInfo[] sessions = mApexService.getSessions(); for (ApexSessionInfo si : sessions) { - ipw.println("Session ID: " + Integer.toString(si.sessionId)); + ipw.println("Session ID: " + si.sessionId); ipw.increaseIndent(); if (si.isUnknown) { ipw.println("State: UNKNOWN"); @@ -206,8 +232,6 @@ class ApexManager { ipw.println("State: STAGED"); } else if (si.isActivated) { ipw.println("State: ACTIVATED"); - } else if (si.isActivationPendingRetry) { - ipw.println("State: ACTIVATION PENDING RETRY"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); } diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java index 5b765dfee3a4..d53d81cf0860 100644 --- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java +++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java @@ -28,7 +28,7 @@ import android.util.ByteStringUtils; import android.util.EventLog; import android.util.Log; -import com.android.server.pm.dex.DexLogger; +import com.android.server.pm.dex.DynamicCodeLogger; import java.util.ArrayList; import java.util.List; @@ -38,9 +38,10 @@ import java.util.regex.Pattern; /** * Scheduled jobs related to logging of app dynamic code loading. The idle logging job runs daily - * while idle and charging and calls {@link DexLogger} to write dynamic code information to the - * event log. The audit watching job scans the event log periodically while idle to find AVC audit - * messages indicating use of dynamic native code and adds the information to {@link DexLogger}. + * while idle and charging and calls {@link DynamicCodeLogger} to write dynamic code information + * to the event log. The audit watching job scans the event log periodically while idle to find AVC + * audit messages indicating use of dynamic native code and adds the information to + * {@link DynamicCodeLogger}. * {@hide} */ public class DynamicCodeLoggingService extends JobService { @@ -130,9 +131,9 @@ public class DynamicCodeLoggingService extends JobService { } } - private static DexLogger getDexLogger() { + private static DynamicCodeLogger getDynamicCodeLogger() { PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package"); - return pm.getDexManager().getDexLogger(); + return pm.getDexManager().getDynamicCodeLogger(); } private class IdleLoggingThread extends Thread { @@ -149,14 +150,14 @@ public class DynamicCodeLoggingService extends JobService { Log.d(TAG, "Starting IdleLoggingJob run"); } - DexLogger dexLogger = getDexLogger(); - for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) { + DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger(); + for (String packageName : dynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) { if (mIdleLoggingStopRequested) { Log.w(TAG, "Stopping IdleLoggingJob run at scheduler request"); return; } - dexLogger.logDynamicCodeLoading(packageName); + dynamicCodeLogger.logDynamicCodeLoading(packageName); } jobFinished(mParams, /* reschedule */ false); @@ -191,7 +192,7 @@ public class DynamicCodeLoggingService extends JobService { private boolean processAuditEvents() { // Scan the event log for SELinux (avc) audit messages indicating when an // (untrusted) app has executed native code from an app data - // file. Matches are recorded in DexLogger. + // file. Matches are recorded in DynamicCodeLogger. // // These messages come from the kernel audit system via logd. (Note that // some devices may not generate these messages at all, or the format may @@ -213,7 +214,7 @@ public class DynamicCodeLoggingService extends JobService { // On each run we process all the matching events in the log. This may // mean re-processing events we have already seen, and in any case there // may be duplicate events for the same app+file. These are de-duplicated - // by DexLogger. + // by DynamicCodeLogger. // // Note that any app can write a message to the event log, including one // that looks exactly like an AVC audit message, so the information may @@ -228,7 +229,7 @@ public class DynamicCodeLoggingService extends JobService { return true; } - DexLogger dexLogger = getDexLogger(); + DynamicCodeLogger dynamicCodeLogger = getDynamicCodeLogger(); List<EventLog.Event> events = new ArrayList<>(); EventLog.readEvents(tags, events); @@ -267,7 +268,7 @@ public class DynamicCodeLoggingService extends JobService { // hex-encodes the bytes; we need to undo that. path = unhex(matcher.group(2)); } - dexLogger.recordNative(uid, path); + dynamicCodeLogger.recordNative(uid, path); } return true; diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS index 33b8641c145e..8ce2568ac3e2 100644 --- a/services/core/java/com/android/server/pm/OWNERS +++ b/services/core/java/com/android/server/pm/OWNERS @@ -9,6 +9,10 @@ svetoslavganov@google.com toddke@android.com toddke@google.com +# apex support +per-file ApexManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com +per-file StagingManager.java = dariofreni@google.com, narayan@google.com, toddke@android.com, toddke@google.com + # dex per-file AbstractStatsBase.java = agampe@google.com per-file AbstractStatsBase.java = calin@google.com @@ -19,6 +23,9 @@ per-file BackgroundDexOptService.java = ngeoffray@google.com per-file CompilerStats.java = agampe@google.com per-file CompilerStats.java = calin@google.com per-file CompilerStats.java = ngeoffray@google.com +per-file DynamicCodeLoggingService.java = agampe@google.com +per-file DynamicCodeLoggingService.java = calin@google.com +per-file DynamicCodeLoggingService.java = ngeoffray@google.com per-file InstructionSets.java = agampe@google.com per-file InstructionSets.java = calin@google.com per-file InstructionSets.java = ngeoffray@google.com diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 86083494f3d6..f5d88e3cf56f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -84,7 +84,7 @@ import com.android.internal.util.ImageUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.IoThread; import com.android.server.LocalServices; -import com.android.server.pm.permission.PermissionManagerInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import libcore.io.IoUtils; @@ -131,7 +131,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final Context mContext; private final PackageManagerService mPm; private final StagingManager mStagingManager; - private final PermissionManagerInternal mPermissionManager; + private final PermissionManagerServiceInternal mPermissionManager; private AppOpsManager mAppOps; @@ -189,7 +189,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) { mContext = context; mPm = pm; - mPermissionManager = LocalServices.getService(PermissionManagerInternal.class); + mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class); mInstallThread = new HandlerThread(TAG); mInstallThread.start(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 400443a16fab..b1c186e9e9f8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -238,7 +238,6 @@ import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; import android.os.storage.VolumeRecord; -import android.permission.PermissionControllerManager; import android.provider.DeviceConfig; import android.provider.MediaStore; import android.provider.Settings.Global; @@ -291,7 +290,6 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; -import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; @@ -315,9 +313,9 @@ import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; -import com.android.server.pm.permission.PermissionManagerInternal; -import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; @@ -371,7 +369,6 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -445,8 +442,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean ENABLE_FREE_CACHE_V2 = SystemProperties.getBoolean("fw.free_cache_v2", true); - private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60); - private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts"; private static final int RADIO_UID = Process.PHONE_UID; @@ -940,7 +935,7 @@ public class PackageManagerService extends IPackageManager.Stub // TODO remove this and go through mPermissonManager directly final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; - private final PermissionManagerInternal mPermissionManager; + private final PermissionManagerServiceInternal mPermissionManager; private final ComponentResolver mComponentResolver; // List of packages names to keep cached, even if they are uninstalled for all users @@ -985,6 +980,9 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mPackages") private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; + @GuardedBy("mPackages") + private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider; + private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> { private Context mContext; private ComponentName mIntentFilterVerifierComponent; @@ -1349,7 +1347,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mRequiredVerifierPackage; final @NonNull String mRequiredInstallerPackage; final @NonNull String mRequiredUninstallerPackage; - final String mRequiredPermissionControllerPackage; + final @NonNull String mRequiredPermissionControllerPackage; final @Nullable String mSetupWizardPackage; final @Nullable String mStorageManagerPackage; final @Nullable String mSystemTextClassifierPackage; @@ -1937,9 +1935,14 @@ public class PackageManagerService extends IPackageManager.Stub } } - // We may also need to apply pending (restored) runtime - // permission grants within these users. - mSettings.applyPendingPermissionGrantsLPw(packageName, userId); + // We may also need to apply pending (restored) runtime permission grants + // within these users. + mPermissionManager.restoreDelayedRuntimePermissions(packageName, + UserHandle.of(userId)); + + // Persistent preferred activity might have came into effect due to this + // install. + updateDefaultHomeLPw(userId); } } } @@ -3306,7 +3309,8 @@ public class PackageManagerService extends IPackageManager.Stub // feature flags should cause us to invalidate any caches. final String cacheName = SystemProperties.digestOf( "ro.build.fingerprint", - "persist.sys.isolated_storage"); + StorageManager.PROP_ISOLATED_STORAGE, + StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -12803,7 +12807,7 @@ public class PackageManagerService extends IPackageManager.Stub public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames, int restrictionFlags, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, - "setPackagesSuspendedAsUser"); + "setDistractingPackageRestrictionsAsUser"); final int callingUid = Binder.getCallingUid(); if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID @@ -18817,7 +18821,7 @@ public class PackageManagerService extends IPackageManager.Stub // permission as requiring a review as this is the initial state. int flags = 0; if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE; } if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) { if (hasInstallState) { @@ -19076,6 +19080,7 @@ public class PackageManagerService extends IPackageManager.Stub pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19226,6 +19231,13 @@ public class PackageManagerService extends IPackageManager.Stub /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @GuardedBy("mPackages") boolean clearPackagePreferredActivitiesLPw(String packageName, int userId) { + return clearPackagePreferredActivitiesLPw(packageName, false, userId); + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + @GuardedBy("mPackages") + private boolean clearPackagePreferredActivitiesLPw(String packageName, + boolean skipUpdateDefaultHome, int userId) { ArrayList<PreferredActivity> removed = null; boolean changed = false; for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { @@ -19254,6 +19266,9 @@ public class PackageManagerService extends IPackageManager.Stub pir.removeFilter(pa); } changed = true; + if (!skipUpdateDefaultHome) { + updateDefaultHomeLPw(thisUserId); + } } } if (changed) { @@ -19313,8 +19328,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer try { synchronized (mPackages) { - clearPackagePreferredActivitiesLPw(null, userId); + clearPackagePreferredActivitiesLPw(null, true, userId); mSettings.applyDefaultPreferredAppsLPw(userId); + updateDefaultHomeLPw(userId); // TODO: We have to reset the default SMS and Phone. This requires // significant refactoring to keep all default apps in the package // manager (cleaner but more work) or have the services provide @@ -19383,6 +19399,7 @@ public class PackageManagerService extends IPackageManager.Stub new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } @@ -19426,6 +19443,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changed) { scheduleWritePackageRestrictionsLocked(userId); postPreferredActivityChangedBroadcast(userId); + updateDefaultHomeLPw(userId); } } } @@ -19513,6 +19531,7 @@ public class PackageManagerService extends IPackageManager.Stub (readParser, readUserId) -> { synchronized (mPackages) { mSettings.readPreferredActivitiesLPw(readParser, readUserId); + updateDefaultHomeLPw(readUserId); } }); } catch (Exception e) { @@ -19568,8 +19587,17 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_DEFAULT_APPS, (parser1, userId1) -> { + String defaultBrowser; synchronized (mPackages) { mSettings.readDefaultAppsLPw(parser1, userId1); + defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); + } + if (defaultBrowser != null) { + PackageManagerInternal.DefaultBrowserProvider provider; + synchronized (mPackages) { + provider = mDefaultBrowserProvider; + } + provider.setDefaultBrowser(defaultBrowser, userId1); } }); } catch (Exception e) { @@ -19633,139 +19661,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public byte[] getPermissionGrantBackup(int userId) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the system may call getPermissionGrantBackup()"); - } - - AtomicReference<byte[]> backup = new AtomicReference<>(); - mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup( - UserHandle.of(userId), mContext.getMainExecutor(), (b) -> { - synchronized (backup) { - backup.set(b); - backup.notifyAll(); - } - }); - - long start = System.currentTimeMillis(); - synchronized (backup) { - while (backup.get() == null) { - long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis(); - if (timeLeft <= 0) { - return null; - } - - try { - backup.wait(timeLeft); - } catch (InterruptedException ignored) { - return null; - } - } - } - - return backup.get(); - } - - @Override - public void restorePermissionGrants(byte[] backup, int userId) { - if (Binder.getCallingUid() != Process.SYSTEM_UID) { - throw new SecurityException("Only the system may call restorePermissionGrants()"); - } - - try { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); - restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP, - (parser1, userId1) -> { - synchronized (mPackages) { - processRestoredPermissionGrantsLPr(parser1, userId1); - } - }); - } catch (Exception e) { - if (DEBUG_BACKUP) { - Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage()); - } - } - } - - @GuardedBy("mPackages") - private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId) - throws XmlPullParserException, IOException { - String pkgName = null; - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - final String tagName = parser.getName(); - if (tagName.equals(TAG_GRANT)) { - pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - if (DEBUG_BACKUP) { - Slog.v(TAG, "+++ Restoring grants for package " + pkgName); - } - } else if (tagName.equals(TAG_PERMISSION)) { - - final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED)); - final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME); - - int newFlagSet = 0; - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { - newFlagSet |= FLAG_PERMISSION_USER_SET; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { - newFlagSet |= FLAG_PERMISSION_USER_FIXED; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { - newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; - } - if (DEBUG_BACKUP) { - Slog.v(TAG, " + Restoring grant:" - + " pkg=" + pkgName - + " perm=" + permName - + " granted=" + isGranted - + " bits=0x" + Integer.toHexString(newFlagSet)); - } - final PackageSetting ps = mSettings.mPackages.get(pkgName); - if (ps != null) { - // Already installed so we apply the grant immediately - if (DEBUG_BACKUP) { - Slog.v(TAG, " + already installed; applying"); - } - PermissionsState perms = ps.getPermissionsState(); - BasePermission bp = - (BasePermission) mPermissionManager.getPermissionTEMP(permName); - if (bp != null) { - if (isGranted) { - perms.grantRuntimePermission(bp, userId); - } - if (newFlagSet != 0) { - perms.updatePermissionFlags( - bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet); - } - } - } else { - // Need to wait for post-restore install to apply the grant - if (DEBUG_BACKUP) { - Slog.v(TAG, " - not yet installed; saving for later"); - } - mSettings.processRestoredPermissionGrantLPr(pkgName, permName, - isGranted, newFlagSet, userId); - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName); - XmlUtils.skipCurrentTag(parser); - } - } - - scheduleWriteSettingsLocked(); - mSettings.writeRuntimePermissionsForUserLPr(userId, false); - } - - @Override public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage, int sourceUserId, int targetUserId, int flags) { mContext.enforceCallingOrSelfPermission( @@ -19927,19 +19822,59 @@ public class PackageManagerService extends IPackageManager.Stub ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates, int userId) { Intent intent = getHomeIntent(); - List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null, + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, PackageManager.GET_META_DATA, userId); - ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0, - true, false, false, userId); - allHomeCandidates.clear(); - if (list != null) { - allHomeCandidates.addAll(list); + if (resolveInfos == null) { + return null; } - return (preferred == null || preferred.activityInfo == null) - ? null - : new ComponentName(preferred.activityInfo.packageName, - preferred.activityInfo.name); + allHomeCandidates.addAll(resolveInfos); + + PackageManagerInternal.DefaultHomeProvider provider; + synchronized (mPackages) { + provider = mDefaultHomeProvider; + } + if (provider == null) { + Slog.e(TAG, "mDefaultHomeProvider is null"); + return null; + } + String packageName = provider.getDefaultHome(userId); + if (packageName == null) { + return null; + } + int resolveInfosSize = resolveInfos.size(); + for (int i = 0; i < resolveInfosSize; i++) { + ResolveInfo resolveInfo = resolveInfos.get(i); + + if (resolveInfo.activityInfo != null && TextUtils.equals( + resolveInfo.activityInfo.packageName, packageName)) { + return new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + } + } + return null; + } + + private void updateDefaultHomeLPw(int userId) { + Intent intent = getHomeIntent(); + List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null, + PackageManager.GET_META_DATA, userId); + ResolveInfo preferredResolveInfo = findPreferredActivity(intent, null, 0, resolveInfos, + 0, true, false, false, userId); + String packageName = preferredResolveInfo != null + && preferredResolveInfo.activityInfo != null + ? preferredResolveInfo.activityInfo.packageName : null; + String currentPackageName = mDefaultHomeProvider.getDefaultHome(userId); + if (TextUtils.equals(currentPackageName, packageName)) { + return; + } + String[] callingPackages = getPackagesForUid(Binder.getCallingUid()); + if (callingPackages != null && ArrayUtils.contains(callingPackages, + mRequiredPermissionControllerPackage)) { + // PermissionController manages default home directly. + return; + } + mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId); } @Override @@ -21213,10 +21148,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) { - mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState); - } - if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) { // XXX should handle packageName != null by dumping only install data that // the given package is involved with. @@ -23829,6 +23760,13 @@ public class PackageManagerService extends IPackageManager.Stub mDefaultBrowserProvider = provider; } } + + @Override + public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { + synchronized (mPackages) { + mDefaultHomeProvider = provider; + } + } } @GuardedBy("mPackages") diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 975ffb25a784..92fe377e9495 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -19,10 +19,6 @@ package com.android.server.pm; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; @@ -249,23 +245,6 @@ public final class Settings { private static final String ATTR_SDK_VERSION = "sdkVersion"; private static final String ATTR_DATABASE_VERSION = "databaseVersion"; - // Bookkeeping for restored permission grants - private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms"; - // package name: ATTR_PACKAGE_NAME - private static final String TAG_PERMISSION_ENTRY = "perm"; - // permission name: ATTR_NAME - // permission granted (boolean): ATTR_GRANTED - private static final String ATTR_USER_SET = "set"; - private static final String ATTR_USER_FIXED = "fixed"; - private static final String ATTR_REVOKE_ON_UPGRADE = "rou"; - private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr"; - - // Flag mask of restored permission grants that are applied at install time - private static final int USER_RUNTIME_GRANT_MASK = - FLAG_PERMISSION_USER_SET - | FLAG_PERMISSION_USER_FIXED - | FLAG_PERMISSION_REVOKE_ON_UPGRADE; - private final Object mLock; private final RuntimePermissionPersistence mRuntimePermissionsPersistence; @@ -303,26 +282,6 @@ public final class Settings { int[] excludedUserIds; } - // Bookkeeping for restored user permission grants - final class RestoredPermissionGrant { - String permissionName; - boolean granted; - int grantBits; - - RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) { - permissionName = name; - granted = isGranted; - grantBits = theGrantBits; - } - } - - // This would be more compact as a flat array of restored grants or something, but we - // may have quite a few, especially during early device lifetime, and avoiding all those - // linear lookups will be important. - private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>> - mRestoredUserGrants = - new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>(); - private static int mFirstAvailableUid = 0; /** Map from volume UUID to {@link VersionInfo} */ @@ -461,43 +420,6 @@ public final class Settings { return mRenamedPackages.put(pkgName, origPkgName); } - void applyPendingPermissionGrantsLPw(String packageName, int userId) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.get(userId); - if (grantsByPackage == null || grantsByPackage.size() == 0) { - return; - } - - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName); - if (grants == null || grants.size() == 0) { - return; - } - - final PackageSetting ps = mPackages.get(packageName); - if (ps == null) { - Slog.e(TAG, "Can't find supposedly installed package " + packageName); - return; - } - final PermissionsState perms = ps.getPermissionsState(); - - for (RestoredPermissionGrant grant : grants) { - BasePermission bp = mPermissions.getPermission(grant.permissionName); - if (bp != null) { - if (grant.granted) { - perms.grantRuntimePermission(bp, userId); - } - perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits); - } - } - - // And remove it from the pending-grant bookkeeping - grantsByPackage.remove(packageName); - if (grantsByPackage.size() < 1) { - mRestoredUserGrants.remove(userId); - } - writeRuntimePermissionsForUserLPr(userId, false); - } - public boolean canPropagatePermissionToInstantApp(String permName) { return mPermissions.canPropagatePermissionToInstantApp(permName); } @@ -1982,13 +1904,6 @@ public final class Settings { } } - // Specifically for backup/restore - public void processRestoredPermissionGrantLPr(String pkgName, String permission, - boolean isGranted, int restoredFlagSet, int userId) { - mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr( - pkgName, permission, isGranted, restoredFlagSet, userId); - } - void writeDefaultAppsLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { serializer.startTag(null, TAG_DEFAULT_APPS); @@ -5014,51 +4929,6 @@ public final class Settings { pw.print(mReadMessages.toString()); } - void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) { - if (mRestoredUserGrants.size() > 0) { - pw.println(); - pw.println("Restored (pending) permission grants:"); - for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.valueAt(userIndex); - if (grantsByPackage != null && grantsByPackage.size() > 0) { - final int userId = mRestoredUserGrants.keyAt(userIndex); - pw.print(" User "); pw.println(userId); - - for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) { - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex); - if (grants != null && grants.size() > 0) { - final String pkgName = grantsByPackage.keyAt(pkgIndex); - pw.print(" "); pw.print(pkgName); pw.println(" :"); - - for (RestoredPermissionGrant g : grants) { - pw.print(" "); - pw.print(g.permissionName); - if (g.granted) { - pw.print(" GRANTED"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { - pw.print(" user_set"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { - pw.print(" user_fixed"); - } - if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { - pw.print(" revoke_on_upgrade"); - } - if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - pw.print(" revoke_when_requested"); - } - pw.println(); - } - } - } - } - } - pw.println(); - } - } - private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) { if (pkg == null) { pw.print("unknown"); @@ -5328,55 +5198,6 @@ public final class Settings { serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); - // Now any restored permission grants that are waiting for the apps - // in question to be installed. These are stored as per-package - // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some - // number of individual permission grant entities. - if (mRestoredUserGrants.get(userId) != null) { - ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants = - mRestoredUserGrants.get(userId); - if (restoredGrants != null) { - final int pkgCount = restoredGrants.size(); - for (int i = 0; i < pkgCount; i++) { - final ArraySet<RestoredPermissionGrant> pkgGrants = - restoredGrants.valueAt(i); - if (pkgGrants != null && pkgGrants.size() > 0) { - final String pkgName = restoredGrants.keyAt(i); - serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); - serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName); - - final int N = pkgGrants.size(); - for (int z = 0; z < N; z++) { - RestoredPermissionGrant g = pkgGrants.valueAt(z); - serializer.startTag(null, TAG_PERMISSION_ENTRY); - serializer.attribute(null, ATTR_NAME, g.permissionName); - - if (g.granted) { - serializer.attribute(null, ATTR_GRANTED, "true"); - } - - if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) { - serializer.attribute(null, ATTR_USER_SET, "true"); - } - if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) { - serializer.attribute(null, ATTR_USER_FIXED, "true"); - } - if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { - serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true"); - } - if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) - != 0) { - serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED, - "true"); - } - serializer.endTag(null, TAG_PERMISSION_ENTRY); - } - serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS); - } - } - } - } - serializer.endDocument(); destination.finishWrite(out); @@ -5455,29 +5276,6 @@ public final class Settings { } } - // Backup/restore support - - public void rememberRestoredUserGrantLPr(String pkgName, String permission, - boolean isGranted, int restoredFlagSet, int userId) { - // This change will be remembered at write-settings time - ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage = - mRestoredUserGrants.get(userId); - if (grantsByPackage == null) { - grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>(); - mRestoredUserGrants.put(userId, grantsByPackage); - } - - ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName); - if (grants == null) { - grants = new ArraySet<RestoredPermissionGrant>(); - grantsByPackage.put(pkgName, grants); - } - - RestoredPermissionGrant grant = new RestoredPermissionGrant(permission, - isGranted, restoredFlagSet); - grants.add(grant); - } - // Private internals @GuardedBy("Settings.this.mLock") @@ -5520,50 +5318,6 @@ public final class Settings { } parsePermissionsLPr(parser, sus.getPermissionsState(), userId); } break; - - case TAG_RESTORED_RUNTIME_PERMISSIONS: { - final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME); - parseRestoredRuntimePermissionsLPr(parser, pkgName, userId); - } break; - } - } - } - - private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser, - final String pkgName, final int userId) throws IOException, XmlPullParserException { - final int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - switch (parser.getName()) { - case TAG_PERMISSION_ENTRY: { - final String permName = parser.getAttributeValue(null, ATTR_NAME); - final boolean isGranted = "true".equals( - parser.getAttributeValue(null, ATTR_GRANTED)); - - int permBits = 0; - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) { - permBits |= FLAG_PERMISSION_USER_SET; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) { - permBits |= FLAG_PERMISSION_USER_FIXED; - } - if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) { - permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE; - } - if ("true".equals(parser.getAttributeValue(null, - ATTR_REVOKE_WHEN_REQUESTED))) { - permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; - } - - if (isGranted || permBits != 0) { - rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId); - } - } break; } } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 30c2281b07f1..d1ebc9400e4d 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -156,12 +156,8 @@ public class StagingManager { boolean success = true; // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier. - if (!sessionContainsApex(session)) { - // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, - // right away. - session.setStagedSessionReady(); - return; - } + // TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs, + // right away. final ApexInfoList apexInfoList = new ApexInfoList(); // APEX checks. For single-package sessions, check if they contain an APEX. For @@ -227,7 +223,8 @@ public class StagingManager { } session.setStagedSessionReady(); - if (!mApexManager.markStagedSessionReady(session.sessionId)) { + if (sessionContainsApex(session) + && !mApexManager.markStagedSessionReady(session.sessionId)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, "APEX staging failed, check logcat messages from apexd for more " + "details."); @@ -250,7 +247,8 @@ public class StagingManager { } private void resumeSession(@NonNull PackageInstallerSession session) { - if (sessionContainsApex(session)) { + boolean hasApex = sessionContainsApex(session); + if (hasApex) { // Check with apexservice whether the apex packages have been activated. ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); if (apexSessionInfo == null) { @@ -274,7 +272,7 @@ public class StagingManager { mBgHandler.post(() -> preRebootVerification(session)); return; } - if (!apexSessionInfo.isActivated) { + if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) { // In all the remaining cases apexd will try to apply the session again at next // boot. Nothing to do here for now. Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied " @@ -290,7 +288,11 @@ public class StagingManager { + "more information."); return; } + session.setStagedSessionApplied(); + if (hasApex) { + mApexManager.markStagedSessionSuccessful(session.sessionId); + } } private String findFirstAPKInDir(File stageDir) { diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index ee6995b11430..3b805d515178 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -86,11 +86,11 @@ public class DexManager { // encode and save the dex usage data. private final PackageDexUsage mPackageDexUsage; - // DexLogger handles recording of dynamic code loading - which is similar to PackageDexUsage - // but records a different aspect of the data. + // DynamicCodeLogger handles recording of dynamic code loading - which is similar to + // PackageDexUsage but records a different aspect of the data. // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't // record class loaders or ISAs.) - private final DexLogger mDexLogger; + private final DynamicCodeLogger mDynamicCodeLogger; private final IPackageManager mPackageManager; private final PackageDexOptimizer mPackageDexOptimizer; @@ -126,11 +126,11 @@ public class DexManager { mPackageDexOptimizer = pdo; mInstaller = installer; mInstallLock = installLock; - mDexLogger = new DexLogger(pms, installer); + mDynamicCodeLogger = new DynamicCodeLogger(pms, installer); } - public DexLogger getDexLogger() { - return mDexLogger; + public DynamicCodeLogger getDynamicCodeLogger() { + return mDynamicCodeLogger; } /** @@ -230,8 +230,8 @@ public class DexManager { if (!primaryOrSplit) { // Record loading of a DEX file from an app data directory. - mDexLogger.recordDex(loaderUserId, dexPath, searchResult.mOwningPackageName, - loadingAppInfo.packageName); + mDynamicCodeLogger.recordDex(loaderUserId, dexPath, + searchResult.mOwningPackageName, loadingAppInfo.packageName); } if (classLoaderContexts != null) { @@ -269,7 +269,7 @@ public class DexManager { loadInternal(existingPackages); } catch (Exception e) { mPackageDexUsage.clear(); - mDexLogger.clear(); + mDynamicCodeLogger.clear(); Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e); } } @@ -320,12 +320,12 @@ public class DexManager { if (mPackageDexUsage.removePackage(packageName)) { mPackageDexUsage.maybeWriteAsync(); } - mDexLogger.removePackage(packageName); + mDynamicCodeLogger.removePackage(packageName); } else { if (mPackageDexUsage.removeUserPackage(packageName, userId)) { mPackageDexUsage.maybeWriteAsync(); } - mDexLogger.removeUserPackage(packageName, userId); + mDynamicCodeLogger.removeUserPackage(packageName, userId); } } @@ -404,9 +404,9 @@ public class DexManager { } try { - mDexLogger.readAndSync(packageToUsersMap); + mDynamicCodeLogger.readAndSync(packageToUsersMap); } catch (Exception e) { - mDexLogger.clear(); + mDynamicCodeLogger.clear(); Slog.w(TAG, "Exception while loading package dynamic code usage. " + "Starting with a fresh state.", e); } @@ -692,7 +692,7 @@ public class DexManager { */ public void writePackageDexUsageNow() { mPackageDexUsage.writeNow(); - mDexLogger.writeNow(); + mDynamicCodeLogger.writeNow(); } /** diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java index 59cc0cfeef45..2c75bcdaa1ef 100644 --- a/services/core/java/com/android/server/pm/dex/DexLogger.java +++ b/services/core/java/com/android/server/pm/dex/DynamicCodeLogger.java @@ -11,7 +11,7 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.server.pm.dex; @@ -44,12 +44,12 @@ import java.util.Map; import java.util.Set; /** - * This class is responsible for logging data about secondary dex files and, despite the name, - * native code executed from an app's private directory. The data logged includes hashes of the - * name and content of each file. + * This class is responsible for logging data about secondary dex files and native code executed + * from an app's private directory. The data logged includes hashes of the name and content of each + * file. */ -public class DexLogger { - private static final String TAG = "DexLogger"; +public class DynamicCodeLogger { + private static final String TAG = "DynamicCodeLogger"; // Event log tag & subtags used for SafetyNet logging of dynamic code loading (DCL) - // see b/63927552. @@ -61,12 +61,12 @@ public class DexLogger { private final PackageDynamicCodeLoading mPackageDynamicCodeLoading; private final Installer mInstaller; - public DexLogger(IPackageManager pms, Installer installer) { + DynamicCodeLogger(IPackageManager pms, Installer installer) { this(pms, installer, new PackageDynamicCodeLoading()); } @VisibleForTesting - DexLogger(IPackageManager pms, Installer installer, + DynamicCodeLogger(IPackageManager pms, Installer installer, PackageDynamicCodeLoading packageDynamicCodeLoading) { mPackageManager = pms; mPackageDynamicCodeLoading = packageDynamicCodeLoading; @@ -217,7 +217,7 @@ public class DexLogger { /** * Record that an app running in the specified uid has executed native code from the file at - * {@link path}. + * {@param path}. */ public void recordNative(int loadingUid, String path) { String[] packages; diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 2036ed73fdf2..dacc6cd0fe76 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -721,7 +721,7 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage( getDefaultSystemHandlerActivityPackage( RingtoneManager.ACTION_RINGTONE_PICKER, userId), - userId, STORAGE_PERMISSIONS); + userId, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS); // TextClassifier Service String textClassifierPackageName = @@ -1462,14 +1462,15 @@ public final class DefaultPermissionGrantPolicy { outGrantExceptions.get(packageName); if (packageExceptions == null) { // The package must be on the system image - if (!isSystemPackage(packageName)) { - Log.w(TAG, "Unknown package:" + packageName); + PackageInfo packageInfo = getSystemPackageInfo(packageName); + if (!isSystemPackage(packageInfo)) { + Log.w(TAG, "Unknown system package:" + packageName); XmlUtils.skipCurrentTag(parser); continue; } // The package must support runtime permissions - if (!doesPackageSupportRuntimePermissions(getSystemPackageInfo(packageName))) { + if (!doesPackageSupportRuntimePermissions(packageInfo)) { Log.w(TAG, "Skipping non supporting runtime permissions package:" + packageName); XmlUtils.skipCurrentTag(parser); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 38940d6241a6..8df5a71de43e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -30,6 +30,7 @@ import static android.app.AppOpsManager.permissionToOpCode; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; @@ -43,6 +44,9 @@ import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS; import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE; import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; + +import static java.util.concurrent.TimeUnit.SECONDS; import android.Manifest; import android.annotation.NonNull; @@ -69,7 +73,9 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; +import android.permission.PermissionControllerManager; import android.permission.PermissionManager; +import android.permission.PermissionManagerInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -77,6 +83,7 @@ import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -91,9 +98,8 @@ import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; import com.android.server.pm.UserManagerService; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy - .DefaultPermissionGrantedCallback; -import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState.PermissionState; import libcore.util.EmptyArray; @@ -106,6 +112,10 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Manages all permissions and handles permissions related tasks. @@ -122,6 +132,8 @@ public class PermissionManagerService { /** Permission grant: grant as runtime a permission that was granted as an install time one. */ private static final int GRANT_UPGRADE = 4; + private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60); + /** Cap the size of permission trees that 3rd party apps can define; in characters of text */ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; /** Empty array to avoid allocations */ @@ -146,6 +158,9 @@ public class PermissionManagerService { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; + /** Permission controller: User space permission management */ + private PermissionControllerManager mPermissionControllerManager; + /** Default permission policy to provide proper behaviour out-of-the-box */ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; @@ -180,6 +195,16 @@ public class PermissionManagerService { @GuardedBy("mLock") private ArrayMap<String, List<String>> mBackgroundPermissions; + /** + * A permission backup might contain apps that are not installed. In this case we delay the + * restoration until the app is installed. + * + * <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where + * there is <u>no more</u> delayed backup left. + */ + @GuardedBy("mLock") + private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray(); + PermissionManagerService(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { @@ -218,29 +243,31 @@ public class PermissionManagerService { } } - LocalServices.addService( - PermissionManagerInternal.class, new PermissionManagerInternalImpl()); + PermissionManagerServiceInternalImpl localService = + new PermissionManagerServiceInternalImpl(); + LocalServices.addService(PermissionManagerServiceInternal.class, localService); + LocalServices.addService(PermissionManagerInternal.class, localService); } /** * Creates and returns an initialized, internal service for use by other components. * <p> * The object returned is identical to the one returned by the LocalServices class using: - * {@code LocalServices.getService(PermissionManagerInternal.class);} + * {@code LocalServices.getService(PermissionManagerServiceInternal.class);} * <p> * NOTE: The external lock is temporary and should be removed. This needs to be a * lock created by the permission manager itself. */ - public static PermissionManagerInternal create(Context context, + public static PermissionManagerServiceInternal create(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { - final PermissionManagerInternal permMgrInt = - LocalServices.getService(PermissionManagerInternal.class); + final PermissionManagerServiceInternal permMgrInt = + LocalServices.getService(PermissionManagerServiceInternal.class); if (permMgrInt != null) { return permMgrInt; } new PermissionManagerService(context, defaultGrantCallback, externalLock); - return LocalServices.getService(PermissionManagerInternal.class); + return LocalServices.getService(PermissionManagerServiceInternal.class); } @Nullable BasePermission getPermission(String permName) { @@ -332,6 +359,74 @@ public class PermissionManagerService { } /** + * Get the state of the runtime permissions as xml file. + * + * <p>Can not be called on main thread. + * + * @param user The user the data should be extracted for + * + * @return The state as a xml file + */ + private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { + CompletableFuture<byte[]> backup = new CompletableFuture<>(); + mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(), + backup::complete); + + try { + return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Slog.e(TAG, "Cannot create permission backup for " + user, e); + return null; + } + } + + /** + * Restore a permission state previously backed up via {@link #backupRuntimePermissions}. + * + * <p>If not all state can be restored, the un-appliable state will be delayed and can be + * applied via {@link #restoreDelayedRuntimePermissions}. + * + * @param backup The state as an xml file + * @param user The user the data should be restored for + */ + private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { + synchronized (mLock) { + mHasNoDelayedPermBackup.delete(user.getIdentifier()); + mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user); + } + } + + /** + * Try to apply permission backup that was previously not applied. + * + * <p>Can not be called on main thread. + * + * @param packageName The package that is newly installed + * @param user The user the package is installed for + * + * @see #restoreRuntimePermissions + */ + private void restoreDelayedRuntimePermissions(@NonNull String packageName, + @NonNull UserHandle user) { + synchronized (mLock) { + if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) { + return; + } + + mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user, + mContext.getMainExecutor(), (hasMoreBackup) -> { + if (hasMoreBackup) { + return; + } + + synchronized (mLock) { + mHasNoDelayedPermBackup.put(user.getIdentifier(), true); + } + }); + } + } + + /** * Returns {@code true} if the permission can be implied from another granted permission. * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions, * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give @@ -741,7 +836,6 @@ public class PermissionManagerService { if (ps == null) { return; } - final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg); final PermissionsState permissionsState = ps.getPermissionsState(); PermissionsState origPermissions = permissionsState; @@ -828,17 +922,9 @@ public class PermissionManagerService { // For all apps normal permissions are install time ones. grant = GRANT_INSTALL; } else if (bp.isRuntime()) { - // If a permission review is required for legacy apps we represent - // their permissions as always granted runtime ones since we need - // to keep the review required permission flag per user while an - // install permission's state is shared across all users. if (origPermissions.hasInstallPermission(bp.getName())) { - // For legacy apps that became modern, install becomes runtime. - grant = GRANT_UPGRADE; - } else if (isLegacySystemApp) { - // For legacy system apps, install becomes runtime. - // We cannot check hasInstallPermission() for system apps since those - // permissions were granted implicitly and not persisted pre-M. + // Before Q we represented some runtime permissions as install permissions, + // in Q we cannot do this anymore. Hence upgrade them all. grant = GRANT_UPGRADE; } else { // For modern apps keep runtime permissions unchanged. @@ -891,110 +977,111 @@ public class PermissionManagerService { } // Grant an install permission. if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { changedInstallPermission = true; } } break; case GRANT_RUNTIME: { - // Grant previously granted runtime permissions. - for (int userId : UserManagerService.getInstance().getUserIds()) { - final PermissionState permissionState = origPermissions + for (int userId : currentUserIds) { + PermissionState permState = origPermissions .getRuntimePermissionState(perm, userId); - int flags = permissionState != null - ? permissionState.getFlags() : 0; - if (origPermissions.hasRuntimePermission(perm, userId)) { - // Don't propagate the permission in a permission review - // mode if the former was revoked, i.e. marked to not - // propagate on upgrade. Note that in a permission review - // mode install permissions are represented as constantly - // granted runtime ones since we need to keep a per user - // state associated with the permission. Also the revoke - // on upgrade flag is no longer applicable and is reset. - final boolean revokeOnUpgrade = (flags & PackageManager - .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0; - if (revokeOnUpgrade) { - flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; - // Since we changed the flags, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); - } - if (!revokeOnUpgrade) { - if (permissionsState.grantRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // If we cannot put the permission as it was, - // we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); - } + int flags = permState != null ? permState.getFlags() : 0; + + boolean wasChanged = false; + + if (appSupportsRuntimePermissions) { + // Remove review flag as it is not necessary anymore + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; } - // If the app supports runtime permissions no need for a review. - if (appSupportsRuntimePermissions - && (flags & PackageManager - .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - // Since we changed the flags, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } else { + if (permState != null && permState.isGranted()) { + if (permissionsState.grantRuntimePermission(bp, userId) + == PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + } } - } else if (!appSupportsRuntimePermissions) { - // For legacy apps that need a permission review, every new - // runtime permission is granted but it is pending a review. - // We also need to review only platform defined runtime - // permissions as these are the only ones the platform knows - // how to disable the API to simulate revocation as legacy - // apps don't expect to run with revoked permissions. - if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) { - if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0 - && !bp.isRemoved()) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; - // We changed the flags, hence have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + } else { + if (permState == null) { + // New permission + if (PLATFORM_PACKAGE_NAME.equals( + bp.getSourcePackageName())) { + if (!bp.isRemoved()) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED + | FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } } } + if (permissionsState.grantRuntimePermission(bp, userId) - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - // We changed the permission, hence have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + != PERMISSION_OPERATION_FAILURE) { + wasChanged = true; } } - // Propagate the permission flags. + + if (wasChanged) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + } + permissionsState.updatePermissionFlags(bp, userId, flags, flags); } } break; case GRANT_UPGRADE: { - // Grant runtime permissions for a previously held install permission. - final PermissionState permissionState = origPermissions + // Upgrade from Pre-Q to Q permission model. Make all permissions + // runtime + PermissionState permState = origPermissions .getInstallPermissionState(perm); - final int flags = - (permissionState != null) ? permissionState.getFlags() : 0; + int flags = (permState != null) ? permState.getFlags() : 0; + // Remove install permission if (origPermissions.revokeInstallPermission(bp) - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - // We will be transferring the permission flags, so clear them. + != PERMISSION_OPERATION_FAILURE) { origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL, PackageManager.MASK_PERMISSION_FLAGS, 0); changedInstallPermission = true; } - // If the permission is not to be promoted to runtime we ignore it and - // also its other flags as they are not applicable to install permissions. - if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) { - for (int userId : currentUserIds) { + for (int userId : currentUserIds) { + boolean wasChanged = false; + + if (appSupportsRuntimePermissions) { + // Remove review flag as it is not necessary anymore + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; + } + + if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) { + flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; + wasChanged = true; + } else { + if (permissionsState.grantRuntimePermission(bp, userId) != + PERMISSION_OPERATION_FAILURE) { + wasChanged = true; + } + } + } else { if (permissionsState.grantRuntimePermission(bp, userId) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - // Transfer the permission flags. - permissionsState.updatePermissionFlags(bp, userId, - flags, flags); - // If we granted the permission, we have to write. - updatedUserIds = ArrayUtils.appendInt( - updatedUserIds, userId); + PERMISSION_OPERATION_FAILURE) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + wasChanged = true; } } + + if (wasChanged) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + } + + permissionsState.updatePermissionFlags(bp, userId, flags, flags); } } break; @@ -1011,7 +1098,7 @@ public class PermissionManagerService { } } else { if (permissionsState.revokeInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { // Also drop the permission flags. permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, PackageManager.MASK_PERMISSION_FLAGS, 0); @@ -1095,6 +1182,8 @@ public class PermissionManagerService { AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); String pkgName = pkg.packageName; + boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion + >= Build.VERSION_CODES.M; int[] users = UserManagerService.getInstance().getUserIds(); int numUsers = users.length; @@ -1119,27 +1208,17 @@ public class PermissionManagerService { if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED)) == 0) { - if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { - if (permissionToOpCode(permission) != OP_NONE) { - setAppOpMode(permission, pkg, userId, MODE_IGNORED); - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking app-op " - + permissionToOp(permission) + " for " + pkgName - + " as it is now requested"); - } - } - } else { + if (supportsRuntimePermissions) { int revokeResult = ps.revokeRuntimePermission(bp, userId); - if (revokeResult - != PermissionsState.PERMISSION_OPERATION_FAILURE) { - + if (revokeResult != PERMISSION_OPERATION_FAILURE) { if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " + permission - + " for " + pkgName + Slog.i(TAG, "Revoking runtime permission " + + permission + " for " + pkgName + " as it is now requested"); } } + } else { + setAppOpMode(permission, pkg, userId, MODE_IGNORED); } List<String> fgPerms = mBackgroundPermissions.get(permission); @@ -1925,7 +2004,7 @@ public class PermissionManagerService { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionGranted(); } @@ -1945,7 +2024,7 @@ public class PermissionManagerService { final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) { - case PermissionsState.PERMISSION_OPERATION_FAILURE: { + case PERMISSION_OPERATION_FAILURE: { return; } @@ -2045,7 +2124,7 @@ public class PermissionManagerService { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.revokeInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionRevoked(); } @@ -2054,7 +2133,7 @@ public class PermissionManagerService { } if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_FAILURE) { + PERMISSION_OPERATION_FAILURE) { return; } @@ -2522,6 +2601,8 @@ public class PermissionManagerService { throw new IllegalStateException("Signature|privileged permissions not in " + "privapp-permissions whitelist: " + mPrivappPermissionsViolations); } + + mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); } private static String getVolumeUuidForPackage(PackageParser.Package pkg) { @@ -2574,7 +2655,7 @@ public class PermissionManagerService { return mBackgroundPermissions; } - private class PermissionManagerInternalImpl extends PermissionManagerInternal { + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { PermissionManagerService.this.systemReady(); @@ -2737,5 +2818,21 @@ public class PermissionManagerService { return mSettings.getPermissionLocked(permName); } } + + @Override + public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) { + return PermissionManagerService.this.backupRuntimePermissions(user); + } + + @Override + public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) { + PermissionManagerService.this.restoreRuntimePermissions(backup, user); + } + + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, + @NonNull UserHandle user) { + PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user); + } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index f4979746bae3..1dd2408686c1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,19 +18,22 @@ package com.android.server.pm.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManager.PermissionInfoFlags; import android.content.pm.PackageParser; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.PackageManager.PermissionInfoFlags; +import android.permission.PermissionManagerInternal; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** - * Internal interfaces to be used by other components within the system server. + * Internal interfaces services. + * + * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes. */ -public abstract class PermissionManagerInternal { +public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal { /** * Callbacks invoked when interesting actions have been taken on a permission. * <p> diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index 0892b32a6b91..2280d3fd9134 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -16,6 +16,9 @@ }, { "include-filter": "android.permission.cts.SplitPermissionTest" + }, + { + "include-filter": "android.permission.cts.PermissionFlagsTest" } ] }, diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index fdcafa77a378..0c6b773396c8 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -42,10 +42,12 @@ class DisplayFoldController { private final WindowManagerInternal mWindowManagerInternal; private final DisplayManagerInternal mDisplayManagerInternal; private final int mDisplayId; + private final Handler mHandler; /** The display area while device is folded. */ private final Rect mFoldedArea; - private final Handler mHandler; + /** The display area to override the original folded area. */ + private Rect mOverrideFoldedArea = new Rect(); private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); @@ -70,14 +72,23 @@ class DisplayFoldController { return; } if (folded) { + Rect foldedArea; + if (!mOverrideFoldedArea.isEmpty()) { + foldedArea = mOverrideFoldedArea; + } else if (!mFoldedArea.isEmpty()) { + foldedArea = mFoldedArea; + } else { + return; + } + mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo); - final int dx = (mNonOverrideDisplayInfo.logicalWidth - mFoldedArea.width()) / 2 - - mFoldedArea.left; - final int dy = (mNonOverrideDisplayInfo.logicalHeight - mFoldedArea.height()) / 2 - - mFoldedArea.top; + final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2 + - foldedArea.left; + final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2 + - foldedArea.top; - mWindowManagerInternal.setForcedDisplaySize(mDisplayId, mFoldedArea.width(), - mFoldedArea.height()); + mWindowManagerInternal.setForcedDisplaySize(mDisplayId, + foldedArea.width(), foldedArea.height()); mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); } else { mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); @@ -114,6 +125,18 @@ class DisplayFoldController { mListeners.unregister(listener); } + void setOverrideFoldedArea(Rect area) { + mOverrideFoldedArea.set(area); + } + + Rect getFoldedArea() { + if (!mOverrideFoldedArea.isEmpty()) { + return mOverrideFoldedArea; + } else { + return mFoldedArea; + } + } + /** * Only used for the case that persist.debug.force_foldable is set. * This is using proximity sensor to simulate the fold state switch. @@ -125,7 +148,7 @@ class DisplayFoldController { return null; } - final DisplayFoldController result = create(displayId); + final DisplayFoldController result = create(context, displayId); sensorManager.registerListener(new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { @@ -141,13 +164,17 @@ class DisplayFoldController { return result; } - static DisplayFoldController create(int displayId) { + static DisplayFoldController create(Context context, int displayId) { final DisplayManagerInternal displayService = LocalServices.getService(DisplayManagerInternal.class); - final DisplayInfo displayInfo = new DisplayInfo(); - displayService.getNonOverrideDisplayInfo(displayId, displayInfo); - final Rect foldedArea = new Rect(0, displayInfo.logicalHeight / 2, - displayInfo.logicalWidth, displayInfo.logicalHeight); + final String configFoldedArea = context.getResources().getString( + com.android.internal.R.string.config_foldedArea); + final Rect foldedArea; + if (configFoldedArea == null || configFoldedArea.isEmpty()) { + foldedArea = new Rect(); + } else { + foldedArea = Rect.unflattenFromString(configFoldedArea); + } return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class), displayService, displayId, foldedArea, DisplayThread.getHandler()); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2e3e3e430839..c87a81db16e4 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -125,6 +125,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; import android.hardware.hdmi.HdmiAudioSystemClient; @@ -1858,7 +1859,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { readConfigurationDependentBehaviors(); if (mLidControlsDisplayFold) { - mDisplayFoldController = DisplayFoldController.create(DEFAULT_DISPLAY); + mDisplayFoldController = DisplayFoldController.create(context, DEFAULT_DISPLAY); } else if (SystemProperties.getBoolean("persist.debug.force_foldable", false)) { mDisplayFoldController = DisplayFoldController.createWithProxSensor(context, DEFAULT_DISPLAY); @@ -3221,6 +3222,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void setOverrideFoldedArea(Rect area) { + if (mDisplayFoldController != null) { + mDisplayFoldController.setOverrideFoldedArea(area); + } + } + + @Override + public Rect getFoldedArea() { + if (mDisplayFoldController != null) { + return mDisplayFoldController.getFoldedArea(); + } + return new Rect(); + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) throws RemoteException { synchronized (mLock) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 870d61b2ab90..d7e4b6cff4d8 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -64,6 +64,7 @@ import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.content.Context; @@ -1470,6 +1471,20 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { default void unregisterDisplayFoldListener(IDisplayFoldListener listener) {} /** + * Overrides the folded area. + * + * @param area the overriding folded area or an empty {@code Rect} to clear the override. + */ + default void setOverrideFoldedArea(@NonNull Rect area) {} + + /** + * Get the display folded area. + */ + default @NonNull Rect getFoldedArea() { + return new Rect(); + } + + /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index 3534cf30e2bf..888dd9992bdf 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -17,10 +17,13 @@ package com.android.server.policy.role; import android.annotation.NonNull; +import android.annotation.UserIdInt; import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.ResolveInfo; import android.os.Debug; import android.provider.Settings; import android.telecom.TelecomManager; @@ -33,6 +36,7 @@ import com.android.internal.util.CollectionUtils; import com.android.server.LocalServices; import com.android.server.role.RoleManagerService; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -54,19 +58,44 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder @NonNull private final Context mContext; - public LegacyRoleResolutionPolicy(Context context) { + public LegacyRoleResolutionPolicy(@NonNull Context context) { mContext = context; } + @NonNull @Override - public List<String> getRoleHolders(String roleName, int userId) { + public List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId) { switch (roleName) { + case RoleManager.ROLE_ASSISTANT: { + String legacyAssistant = Settings.Secure.getStringForUser( + mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); + if (legacyAssistant == null || legacyAssistant.isEmpty()) { + return Collections.emptyList(); + } else { + return Collections.singletonList( + ComponentName.unflattenFromString(legacyAssistant).getPackageName()); + } + } + case RoleManager.ROLE_BROWSER: { + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( + userId); + return CollectionUtils.singletonOrEmpty(packageName); + } + case RoleManager.ROLE_DIALER: { + String setting = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); + return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) + ? setting + : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); + } case RoleManager.ROLE_SMS: { // Moved over from SmsApplication#getApplication String result = Settings.Secure.getStringForUser( mContext.getContentResolver(), Settings.Secure.SMS_DEFAULT_APPLICATION, userId); - // TODO: STOPSHIP: Remove the following code once we read the value of // config_defaultSms in RoleControllerService. if (result == null) { @@ -92,34 +121,13 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder SmsApplication.SmsApplicationData app = applicationData; result = app == null ? null : app.mPackageName; } - return CollectionUtils.singletonOrEmpty(result); } - case RoleManager.ROLE_ASSISTANT: { - String legacyAssistant = Settings.Secure.getStringForUser( - mContext.getContentResolver(), Settings.Secure.ASSISTANT, userId); - - if (legacyAssistant == null || legacyAssistant.isEmpty()) { - return Collections.emptyList(); - } else { - return Collections.singletonList( - ComponentName.unflattenFromString(legacyAssistant).getPackageName()); - } - } - case RoleManager.ROLE_DIALER: { - String setting = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.DIALER_DEFAULT_APPLICATION, userId); - - return CollectionUtils.singletonOrEmpty(!TextUtils.isEmpty(setting) - ? setting - : mContext.getSystemService(TelecomManager.class).getSystemDialerPackage()); - } - case RoleManager.ROLE_BROWSER: { - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - String packageName = packageManagerInternal.removeLegacyDefaultBrowserPackageName( - userId); + case RoleManager.ROLE_HOME: { + PackageManager packageManager = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = new ArrayList<>(); + ComponentName componentName = packageManager.getHomeActivities(resolveInfos); + String packageName = componentName != null ? componentName.getPackageName() : null; return CollectionUtils.singletonOrEmpty(packageName); } default: { diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java index 4186154016e2..8740256af04d 100644 --- a/services/core/java/com/android/server/power/AttentionDetector.java +++ b/services/core/java/com/android/server/power/AttentionDetector.java @@ -123,6 +123,9 @@ public class AttentionDetector { public AttentionDetector(Runnable onUserAttention, Object lock) { mOnUserAttention = onUserAttention; mLock = lock; + + // Device starts with an awake state upon boot. + mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE; } public void systemReady(Context context) { @@ -145,7 +148,7 @@ public class AttentionDetector { if (DEBUG) { Slog.d(TAG, "Do not check for attention yet, wait " + (whenToCheck - now)); } - return nextScreenDimming; + return whenToCheck; } else if (whenToStopExtending < whenToCheck) { if (DEBUG) { Slog.d(TAG, "Let device sleep to avoid false results and improve security " diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 1782b6a8aa78..176dbbf6a965 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -538,6 +538,9 @@ public final class PowerManagerService extends SystemService // True if we are currently in VR Mode. private boolean mIsVrModeEnabled; + // True if we in the process of performing a forceSuspend + private boolean mForceSuspendActive; + private final class ForegroundProfileObserver extends SynchronousUserSwitchObserver { @Override public void onUserSwitching(int newUserId) throws RemoteException {} @@ -684,6 +687,11 @@ public final class PowerManagerService extends SystemService public void nativeSetFeature(int featureId, int data) { PowerManagerService.nativeSetFeature(featureId, data); } + + /** Wrapper for PowerManager.nativeForceSuspend */ + public boolean nativeForceSuspend() { + return PowerManagerService.nativeForceSuspend(); + } } @VisibleForTesting @@ -718,6 +726,7 @@ public final class PowerManagerService extends SystemService private static native void nativeSetAutoSuspend(boolean enable); private static native void nativeSendPowerHint(int hintId, int data); private static native void nativeSetFeature(int featureId, int data); + private static native boolean nativeForceSuspend(); public PowerManagerService(Context context) { this(context, new Injector()); @@ -1427,7 +1436,7 @@ public final class PowerManagerService extends SystemService } if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE - || !mBootCompleted || !mSystemReady) { + || !mBootCompleted || !mSystemReady || mForceSuspendActive) { return false; } @@ -1463,8 +1472,13 @@ public final class PowerManagerService extends SystemService } } - // This method is called goToSleep for historical reasons but we actually start - // dozing before really going to sleep. + /** + * Puts the system in doze. + * + * This method is called goToSleep for historical reasons but actually attempts to DOZE, + * and only tucks itself in to SLEEP if requested with the flag + * {@link PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE}. + */ @SuppressWarnings("deprecation") private boolean goToSleepNoUpdateLocked(long eventTime, int reason, int flags, int uid) { if (DEBUG_SPEW) { @@ -1481,35 +1495,10 @@ public final class PowerManagerService extends SystemService Trace.traceBegin(Trace.TRACE_TAG_POWER, "goToSleep"); try { - switch (reason) { - case PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN: - Slog.i(TAG, "Going to sleep due to device administration policy " - + "(uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_TIMEOUT: - Slog.i(TAG, "Going to sleep due to screen timeout (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH: - Slog.i(TAG, "Going to sleep due to lid switch (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON: - Slog.i(TAG, "Going to sleep due to power button (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON: - Slog.i(TAG, "Going to sleep due to sleep button (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_HDMI: - Slog.i(TAG, "Going to sleep due to HDMI standby (uid " + uid +")..."); - break; - case PowerManager.GO_TO_SLEEP_REASON_ACCESSIBILITY: - Slog.i(TAG, "Going to sleep by an accessibility service request (uid " - + uid +")..."); - break; - default: - Slog.i(TAG, "Going to sleep by application request (uid " + uid +")..."); - reason = PowerManager.GO_TO_SLEEP_REASON_APPLICATION; - break; - } + reason = Math.min(PowerManager.GO_TO_SLEEP_REASON_MAX, + Math.max(reason, PowerManager.GO_TO_SLEEP_REASON_MIN)); + Slog.i(TAG, "Going to sleep due to " + PowerManager.sleepReasonToString(reason) + + " (uid " + uid + ")..."); mLastSleepTime = eventTime; mLastSleepReason = reason; @@ -3063,10 +3052,10 @@ public final class PowerManagerService extends SystemService if (appid >= Process.FIRST_APPLICATION_UID) { // Cached inactive processes are never allowed to hold wake locks. if (mConstants.NO_CACHED_WAKE_LOCKS) { - disabled = !wakeLock.mUidState.mActive && - wakeLock.mUidState.mProcState + disabled = mForceSuspendActive + || (!wakeLock.mUidState.mActive && wakeLock.mUidState.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT && - wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER; + wakeLock.mUidState.mProcState > ActivityManager.PROCESS_STATE_RECEIVER); } if (mDeviceIdleMode) { // If we are in idle mode, we will also ignore all partial wake locks that are @@ -3241,6 +3230,34 @@ public final class PowerManagerService extends SystemService } } + private boolean forceSuspendInternal(int uid) { + try { + synchronized (mLock) { + mForceSuspendActive = true; + // Place the system in an non-interactive state + goToSleepInternal(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_FORCE_SUSPEND, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, uid); + + // Disable all the partial wake locks as well + updateWakeLockDisabledStatesLocked(); + } + + Slog.i(TAG, "Force-Suspending (uid " + uid + ")..."); + boolean success = mNativeWrapper.nativeForceSuspend(); + if (!success) { + Slog.i(TAG, "Force-Suspending failed in native."); + } + return success; + } finally { + synchronized (mLock) { + mForceSuspendActive = false; + // Re-enable wake locks once again. + updateWakeLockDisabledStatesLocked(); + } + } + } + /** * Low-level function turn the device off immediately, without trying * to be clean. Most people should use {@link ShutdownThread} for a clean shutdown. @@ -4743,6 +4760,20 @@ public final class PowerManagerService extends SystemService } } + @Override // binder call + public boolean forceSuspend() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + + final int uid = Binder.getCallingUid(); + final long ident = Binder.clearCallingIdentity(); + try { + return forceSuspendInternal(uid); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + @Override // Binder call protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 21bf9de5c8b0..c145a22de6cd 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -111,7 +111,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C /** @see #getRoleHolders(String, int) */ public interface RoleHoldersResolver { /** @return a list of packages that hold a given role for a given user */ - List<String> getRoleHolders(String roleName, int userId); + @NonNull + List<String> getRoleHolders(@NonNull String roleName, @UserIdInt int userId); } /** @@ -154,6 +155,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); + packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); registerUserRemovedReceiver(); } @@ -194,7 +196,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } performInitialGrantsIfNecessary(userId); } - }, UserHandle.SYSTEM, intentFilter, null /* broadcastPermission */, null /* handler */); + }, UserHandle.ALL, intentFilter, null, null); getContext().getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED), false, @@ -741,4 +743,33 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } } + + private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider { + + @Nullable + @Override + public String getDefaultHome(@UserIdInt int userId) { + return CollectionUtils.firstOrNull(getOrCreateUserState(userId).getRoleHolders( + RoleManager.ROLE_HOME)); + } + + @Override + public void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId) { + IRoleManagerCallback callback = new IRoleManagerCallback.Stub() { + @Override + public void onSuccess() {} + @Override + public void onFailure() { + Slog.e(LOG_TAG, "Failed to set default home: " + packageName); + } + }; + if (packageName != null) { + getOrCreateControllerService(userId).onAddRoleHolder(RoleManager.ROLE_HOME, + packageName, 0, callback); + } else { + getOrCreateControllerService(userId).onClearRoleHolders(RoleManager.ROLE_HOME, 0, + callback); + } + } + } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 95c3f4c43313..05d3c17e5c25 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -16,6 +16,7 @@ package com.android.server.rollback; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -40,6 +41,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.provider.DeviceConfig; import android.util.IntArray; import android.util.Log; import android.util.SparseBooleanArray; @@ -61,6 +63,7 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; /** * Implementation of service that manages APK level rollbacks. @@ -71,13 +74,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // Rollbacks expire after 48 hours. // TODO: How to test rollback expiration works properly? - private static final long ROLLBACK_LIFETIME_DURATION_MILLIS = 48 * 60 * 60 * 1000; + private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS = + TimeUnit.HOURS.toMillis(48); // Lock used to synchronize accesses to in-memory rollback data // structures. By convention, methods with the suffix "Locked" require // mLock is held when they are called. private final Object mLock = new Object(); + // No need for guarding with lock because value is only accessed in handler thread + // and the value will be written on boot complete. Initialization here happens before + // handler threads are running so that's fine. + private long mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + // Used for generating rollback IDs. private final Random mRandom = new SecureRandom(); @@ -484,7 +493,25 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { }); } + private void updateRollbackLifetimeDurationInMillis() { + String strRollbackLifetimeInMillis = DeviceConfig.getProperty( + DeviceConfig.Rollback.BOOT_NAMESPACE, + DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS); + + try { + mRollbackLifetimeDurationInMillis = (strRollbackLifetimeInMillis == null) + ? DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS + : Long.parseLong(strRollbackLifetimeInMillis); + } catch (NumberFormatException e) { + mRollbackLifetimeDurationInMillis = DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS; + } + } + void onBootCompleted() { + getHandler().post(() -> updateRollbackLifetimeDurationInMillis()); + // Also posts to handler thread + scheduleExpiration(0); + getHandler().post(() -> { // Check to see if any staged sessions with rollback enabled have // been applied. @@ -565,8 +592,6 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (RollbackInfo info : mRecentlyExecutedRollbacks) { mAllocatedRollbackIds.put(info.getRollbackId(), true); } - - scheduleExpiration(0); } /** @@ -700,8 +725,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (!data.isAvailable) { continue; } - - if (!now.isBefore(data.timestamp.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS))) { + if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) { iter.remove(); deleteRollback(data); } else if (oldest == null || oldest.isAfter(data.timestamp)) { @@ -711,7 +735,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (oldest != null) { - scheduleExpiration(now.until(oldest.plusMillis(ROLLBACK_LIFETIME_DURATION_MILLIS), + scheduleExpiration(now.until(oldest.plusMillis(mRollbackLifetimeDurationInMillis), ChronoUnit.MILLIS)); } } @@ -819,6 +843,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { String packageName = newPackage.packageName; for (PackageRollbackInfo info : rd.packages) { if (info.getPackageName().equals(packageName)) { + info.getInstalledUsers().addAll(IntArray.wrap(installedUsers)); AppDataRollbackHelper.SnapshotAppDataResult rs = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); info.getPendingBackups().addAll(rs.pendingBackups); @@ -851,7 +876,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * the child sessions, not the parent session. */ private boolean enableRollbackForSession(PackageInstaller.SessionInfo session, - int[] installedUsers, boolean snapshotUserData) { + @NonNull int[] installedUsers, boolean snapshotUserData) { // TODO: Don't attempt to enable rollback for split installs. final int installFlags = session.installFlags; if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) { @@ -993,7 +1018,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (!session.isMultiPackage()) { - if (!enableRollbackForSession(session, null, false)) { + if (!enableRollbackForSession(session, new int[0], false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1007,7 +1032,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { result.offer(false); return; } - if (!enableRollbackForSession(childSession, null, false)) { + if (!enableRollbackForSession(childSession, new int[0], false)) { Log.e(TAG, "Unable to enable rollback for session: " + sessionId); result.offer(false); return; @@ -1144,8 +1169,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { packages.add(data.packages.get(i).getPackageName()); } mPackageHealthObserver.startObservingHealth(packages, - ROLLBACK_LIFETIME_DURATION_MILLIS); - scheduleExpiration(ROLLBACK_LIFETIME_DURATION_MILLIS); + mRollbackLifetimeDurationInMillis); + scheduleExpiration(mRollbackLifetimeDurationInMillis); } catch (IOException e) { Log.e(TAG, "Unable to enable rollback", e); deleteRollback(data); diff --git a/services/core/java/com/android/server/rollback/TEST_MAPPING b/services/core/java/com/android/server/rollback/TEST_MAPPING index c1d95ac85c23..6be93a0a199b 100644 --- a/services/core/java/com/android/server/rollback/TEST_MAPPING +++ b/services/core/java/com/android/server/rollback/TEST_MAPPING @@ -2,6 +2,9 @@ "presubmit": [ { "name": "RollbackTest" + }, + { + "name": "StagedRollbackTest" } ] } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index f3393e2f29da..4815e5cd7b83 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -85,7 +85,9 @@ import android.os.SystemProperties; import android.os.Temperature; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.DiskInfo; import android.os.storage.StorageManager; +import android.os.storage.VolumeInfo; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -1942,6 +1944,41 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private void pullTimeZoneDataInfo(int tagId, + long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) { + String tzDbVersion = "Unknown"; + try { + tzDbVersion = android.icu.util.TimeZone.getTZDataVersion(); + } catch (Exception e) { + Log.e(TAG, "Getting tzdb version failed: ", e); + } + + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeString(tzDbVersion); + pulledData.add(e); + } + + private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + StorageManager storageManager = mContext.getSystemService(StorageManager.class); + if (storageManager != null) { + List<VolumeInfo> volumes = storageManager.getVolumes(); + for (VolumeInfo vol : volumes) { + final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); + final DiskInfo diskInfo = vol.getDisk(); + if (diskInfo != null && diskInfo.isSd()) { + if (envState.equals(Environment.MEDIA_MOUNTED)) { + StatsLogEventWrapper e = + new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(vol.getType() + 1); + e.writeLong(diskInfo.size); + pulledData.add(e); + } + } + } + } + } + /** * Pulls various data. */ @@ -2130,6 +2167,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.TIME_ZONE_DATA_INFO: { + pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.SDCARD_INFO: { + pullSDCardInfo(tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java index ff01d46e6909..b12129835ca1 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java +++ b/services/core/java/com/android/server/statusbar/StatusBarShellCommand.java @@ -19,6 +19,7 @@ import static android.app.StatusBarManager.DEFAULT_SETUP_DISABLE_FLAGS; import static android.app.StatusBarManager.DISABLE2_NONE; import static android.app.StatusBarManager.DISABLE_NONE; +import android.app.StatusBarManager.DisableInfo; import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -26,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ShellCommand; import android.service.quicksettings.TileService; +import android.util.Pair; import java.io.PrintWriter; @@ -68,6 +70,8 @@ public class StatusBarShellCommand extends ShellCommand { return runGetStatusIcons(); case "disable-for-setup": return runDisableForSetup(); + case "send-disable-flag": + return runSendDisableFlag(); default: return handleDefaultCommands(cmd); } @@ -132,6 +136,47 @@ public class StatusBarShellCommand extends ShellCommand { return 0; } + private int runSendDisableFlag() { + String pkg = mContext.getPackageName(); + int disable1 = DISABLE_NONE; + int disable2 = DISABLE2_NONE; + + DisableInfo info = new DisableInfo(); + + String arg = getNextArg(); + while (arg != null) { + switch (arg) { + case "search": + info.setSearchDisabled(true); + break; + case "home": + info.setNagivationHomeDisabled(true); + break; + case "recents": + info.setRecentsDisabled(true); + break; + case "notification-alerts": + info.setNotificationPeekingDisabled(true); + break; + case "statusbar-expansion": + info.setStatusBarExpansionDisabled(true); + break; + + default: + break; + } + + arg = getNextArg(); + } + + Pair<Integer, Integer> flagPair = info.toFlags(); + + mInterface.disable(flagPair.first, sToken, pkg); + mInterface.disable2(flagPair.second, sToken, pkg); + + return 0; + } + @Override public void onHelp() { final PrintWriter pw = getOutPrintWriter(); @@ -166,6 +211,17 @@ public class StatusBarShellCommand extends ShellCommand { pw.println(" disable-for-setup DISABLE"); pw.println(" If true, disable status bar components unsuitable for device setup"); pw.println(""); + pw.println(" send-disable-flag FLAG..."); + pw.println(" Send zero or more disable flags (parsed individually) to StatusBarManager"); + pw.println(" Valid options:"); + pw.println(" <blank> - equivalent to \"none\""); + pw.println(" none - re-enables all components"); + pw.println(" search - disable search"); + pw.println(" home - disable naviagation home"); + pw.println(" recents - disable recents/overview"); + pw.println(" notification-peek - disable notification peeking"); + pw.println(" statusbar-expansion - disable status bar expansion"); + pw.println(""); } /** diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index 7a3f030f9dd7..f581bc0bca46 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -44,7 +44,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; -import com.android.server.pm.permission.PermissionManagerInternal; +import com.android.server.pm.permission.PermissionManagerServiceInternal; /** * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup @@ -133,7 +133,7 @@ public class TelecomLoaderService extends SystemService { } private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { - return LocalServices.getService(PermissionManagerInternal.class) + return LocalServices.getService(PermissionManagerServiceInternal.class) .getDefaultPermissionGrantPolicy(); } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 423ec4c869c1..c7044a15dd5d 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -366,17 +366,22 @@ public class TrustManagerService extends SystemService { } catch (RemoteException e) { } - if (mSettingsObserver.getTrustAgentsExtendUnlock()) { - trusted = trusted && (!showingKeyguard || isFromUnlock) && userId == mCurrentUser; - if (DEBUG) { - Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted) - + " && " + Boolean.toString(!showingKeyguard) - + " && " + Boolean.toString(userId == mCurrentUser)); - } - } - boolean changed; synchronized (mUserIsTrusted) { + if (mSettingsObserver.getTrustAgentsExtendUnlock()) { + // In extend unlock trust agents can only set the device to trusted if it already + // trusted or the device is unlocked. Attempting to set the device as trusted + // when the device is locked will be ignored. + changed = mUserIsTrusted.get(userId) != trusted; + trusted = trusted + && (!showingKeyguard || isFromUnlock || !changed) + && userId == mCurrentUser; + if (DEBUG) { + Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted) + + " && " + Boolean.toString(!showingKeyguard) + + " && " + Boolean.toString(userId == mCurrentUser)); + } + } changed = mUserIsTrusted.get(userId) != trusted; mUserIsTrusted.put(userId, trusted); } diff --git a/services/core/java/com/android/server/utils/FlagNamespaceUtils.java b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java new file mode 100644 index 000000000000..f26121eac939 --- /dev/null +++ b/services/core/java/com/android/server/utils/FlagNamespaceUtils.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import android.annotation.Nullable; +import android.provider.DeviceConfig; + +import com.android.server.RescueParty; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utilities for interacting with the {@link android.provider.DeviceConfig}. + * + * @hide + */ +public final class FlagNamespaceUtils { + /** + * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that + * Settings were reset by the RescueParty, no actual namespace with this name exists in + * {@link DeviceConfig}. + */ + public static final String NAMESPACE_NO_PACKAGE = "no_package"; + + /** + * Name of the special namespace in DeviceConfig table used for communicating resets. + */ + private static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace"; + /** + * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link + * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the + * first time flags are written to the new namespace in the {@link DeviceConfig}. + */ + private static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces"; + /** + * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter + * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently + * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given + * namespace flags are reset. + */ + private static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package"; + private static final String DELIMITER = ":"; + /** + * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG} + * when communicating recently reset by the RescueParty namespace values. + */ + private static final int MAX_COUNTER_VALUE = 50; + + private static int sKnownResetNamespacesFlagCounter = -1; + + /** + * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with + * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace + * in the consumed namespacesList. These flags are used for communicating the namespaces + * (aka platform packages) whose flags in {@link DeviceConfig} were just reset + * by the RescueParty. + */ + public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) { + if (namespacesList == null) { + return; + } + for (String namespace : namespacesList) { + addToKnownResetNamespaces(namespace); + } + } + + /** + * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with + * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace. + * This flag is used for communicating the namespace (aka platform package) whose flags + * in {@link DeviceConfig} were just reset by the RescueParty. + */ + public static void addToKnownResetNamespaces(String namespace) { + int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter(); + DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY, + RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter, + namespace, /*makeDefault=*/ true); + } + + /** + * Reset all namespaces in DeviceConfig with consumed resetMode. + */ + public static void resetDeviceConfig(int resetMode) { + List<String> allKnownNamespaces = getAllKnownDeviceConfigNamespacesList(); + for (String namespace : allKnownNamespaces) { + DeviceConfig.resetToDefaults(resetMode, namespace); + } + addToKnownResetNamespaces(allKnownNamespaces); + } + + /** + * Returns a list of all known DeviceConfig namespaces, except for the special {@link + * #NAMESPACE_RESCUE_PARTY} + */ + private static List<String> getAllKnownDeviceConfigNamespacesList() { + String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY, + ALL_KNOWN_NAMESPACES_FLAG); + List<String> namespacesList = toStringList(namespacesStr); + namespacesList.remove(NAMESPACE_RESCUE_PARTY); + return namespacesList; + } + + private static List<String> toStringList(String serialized) { + if (serialized == null || serialized.length() == 0) { + return new ArrayList<>(); + } + return Arrays.asList(serialized.split(DELIMITER)); + } + + private static int incrementAndRetrieveResetNamespacesFlagCounter() { + sKnownResetNamespacesFlagCounter++; + if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) { + sKnownResetNamespacesFlagCounter = 0; + } + return sKnownResetNamespacesFlagCounter; + } +} diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index b0ef8a0d4209..071dde74f103 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -2243,12 +2243,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub synchronized (mLock) { mInAmbientMode = inAmbientMode; final WallpaperData data = mWallpaperMap.get(mCurrentUserId); - final boolean hasConnection = data != null && data.connection != null; - final WallpaperInfo info = hasConnection ? data.connection.mInfo : null; - // The wallpaper info is null for image wallpaper, also use the engine in this case. - if (hasConnection && (info == null && isAodImageWallpaperEnabled() - || info != null && info.supportsAmbientMode())) { + if (data != null && data.connection != null && (data.connection.mInfo == null + || data.connection.mInfo.supportsAmbientMode())) { // TODO(multi-display) Extends this method with specific display. engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine; } else { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 0251efb872bc..087de69b6c12 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -87,8 +87,6 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; import static android.os.Process.SYSTEM_UID; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; -import static android.view.WindowManagerPolicyConstants.NAV_BAR_RIGHT; import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER; import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK; @@ -2677,8 +2675,9 @@ final class ActivityRecord extends ConfigurationContainer { * Get the configuration orientation by the requested screen orientation * ({@link ActivityInfo.ScreenOrientation}) of this activity. * - * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}, - * {@link #ORIENTATION_UNDEFINED}). + * @return orientation in ({@link Configuration#ORIENTATION_LANDSCAPE}, + * {@link Configuration#ORIENTATION_PORTRAIT}, + * {@link Configuration#ORIENTATION_UNDEFINED}). */ int getRequestedConfigurationOrientation() { final int screenOrientation = getOrientation(); @@ -2827,28 +2826,6 @@ final class ActivityRecord extends ConfigurationContainer { outAppBounds.setEmpty(); } - // TODO(b/112288258): Remove below calculation because the position information in bounds - // will be replaced by the offset of surface. - final Rect appBounds = parentConfig.windowConfiguration.getAppBounds(); - if (appBounds != null) { - final Rect outBounds = inOutConfig.windowConfiguration.getBounds(); - final int activityWidth = outBounds.width(); - final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId()); - if (navBarPosition == NAV_BAR_LEFT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = appBounds.right - activityWidth; - outBounds.right = appBounds.right; - } else if (navBarPosition == NAV_BAR_RIGHT) { - // Position the activity frame on the opposite side of the nav bar. - outBounds.left = 0; - outBounds.right = activityWidth + appBounds.left; - } else if (appBounds.width() > activityWidth) { - // Horizontally center the frame. - outBounds.left = appBounds.left + (appBounds.width() - activityWidth) / 2; - outBounds.right = outBounds.left + activityWidth; - } - } - task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds); } @@ -2936,14 +2913,36 @@ final class ActivityRecord extends ConfigurationContainer { // should be given the aspect ratio. activityWidth = (int) ((activityHeight * maxAspectRatio) + 0.5f); } - } else if (containingRatio < minAspectRatio && minAspectRatio != 0) { - if (containingAppWidth < containingAppHeight) { - // Width is the shorter side, so we use the height to figure-out what the max. width - // should be given the aspect ratio. + } else if (containingRatio < minAspectRatio) { + boolean adjustWidth; + switch (getRequestedConfigurationOrientation()) { + case ORIENTATION_LANDSCAPE: + // Width should be the longer side for this landscape app, so we use the width + // to figure-out what the max. height should be given the aspect ratio. + adjustWidth = false; + break; + case ORIENTATION_PORTRAIT: + // Height should be the longer side for this portrait app, so we use the height + // to figure-out what the max. width should be given the aspect ratio. + adjustWidth = true; + break; + default: + // This app doesn't have a preferred orientation, so we keep the length of the + // longer side, and use it to figure-out the length of the shorter side. + if (containingAppWidth < containingAppHeight) { + // Width is the shorter side, so we use the height to figure-out what the + // max. width should be given the aspect ratio. + adjustWidth = true; + } else { + // Height is the shorter side, so we use the width to figure-out what the + // max. height should be given the aspect ratio. + adjustWidth = false; + } + break; + } + if (adjustWidth) { activityWidth = (int) ((activityHeight / minAspectRatio) + 0.5f); } else { - // Height is the shorter side, so we use the width to figure-out what the max. - // height should be given the aspect ratio. activityHeight = (int) ((activityWidth / minAspectRatio) + 0.5f); } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 0a3c2fba3930..c685b05e99e0 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -2332,6 +2332,20 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { if (!task.canBeLaunchedOnDisplay(actualDisplayId)) { throw new IllegalStateException("Task resolved to incompatible display"); } + + final ActivityDisplay preferredDisplay = + mRootActivityContainer.getActivityDisplay(preferredDisplayId); + + final boolean singleTaskInstance = preferredDisplay != null + && preferredDisplay.isSingleTaskInstance(); + + if (singleTaskInstance) { + // Suppress the warning toast if the preferredDisplay was set to singleTask. + // The singleTaskInstance displays will only contain one task and any attempt to + // launch new task will re-route to the default display. + return; + } + if (preferredDisplayId != actualDisplayId) { Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId); // Display a warning toast that we failed to put a task on a secondary display. diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index c33a2c179ab7..538813e9b21c 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -98,6 +98,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; @@ -115,6 +116,7 @@ import android.util.Pools.SynchronizedPool; import android.util.Slog; import android.widget.Toast; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; @@ -761,11 +763,11 @@ class ActivityStarter { abort |= (abortBackgroundStart && !mService.isBackgroundActivityStartsEnabled()); // TODO: remove this toast after feature development is done if (abortBackgroundStart) { - final String toastMsg = abort - ? "Background activity start from " + callingPackage - + " blocked. See go/q-bg-block." - : "This background activity start from " + callingPackage - + " will be blocked in future Q builds. See go/q-bg-block."; + final Resources res = mService.mContext.getResources(); + final String toastMsg = res.getString(abort + ? R.string.activity_starter_block_bg_activity_starts_enforcing + : R.string.activity_starter_block_bg_activity_starts_permissive, + callingPackage); mService.mUiHandler.post(() -> { Toast.makeText(mService.mContext, toastMsg, Toast.LENGTH_LONG).show(); }); @@ -830,12 +832,25 @@ class ActivityStarter { new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT, null); - final int flags = intent.getFlags(); Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); - newIntent.setFlags(flags - | FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_MULTIPLE_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + int flags = intent.getFlags(); + flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + + /* + * Prevent reuse of review activity: Each app needs their own review activity. By + * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities + * with the same launch parameters (extras are ignored). Hence to avoid possible + * reuse force a new activity via the MULTIPLE_TASK flag. + * + * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used, + * hence no need to add the flag in this case. + */ + if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) { + flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + } + newIntent.setFlags(flags); + newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName); newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); if (resultRecord != null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 258819fdece9..df73d1dbc7c6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -2467,7 +2467,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final Rect destBounds = new Rect(); stack.getAnimationOrCurrentBounds(destBounds); - if (!destBounds.isEmpty() || !destBounds.equals(compareBounds)) { + if (destBounds.isEmpty() || !destBounds.equals(compareBounds)) { Slog.w(TAG, "The current stack bounds does not matched! It may be obsolete."); return; } @@ -3202,23 +3202,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void exitFreeformMode(IBinder token) { + public void toggleFreeformWindowingMode(IBinder token) { synchronized (mGlobalLock) { long ident = Binder.clearCallingIdentity(); try { final ActivityRecord r = ActivityRecord.forTokenLocked(token); if (r == null) { throw new IllegalArgumentException( - "exitFreeformMode: No activity record matching token=" + token); + "toggleFreeformWindowingMode: No activity record matching token=" + + token); } final ActivityStack stack = r.getActivityStack(); - if (stack == null || !stack.inFreeformWindowingMode()) { - throw new IllegalStateException( - "exitFreeformMode: You can only go fullscreen from freeform."); + if (stack == null) { + throw new IllegalStateException("toggleFreeformWindowingMode: the activity " + + "doesn't have a stack"); + } + + if (!stack.inFreeformWindowingMode() + && stack.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { + throw new IllegalStateException("toggleFreeformWindowingMode: You can only " + + "toggle between fullscreen and freeform."); } - stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + if (stack.inFreeformWindowingMode()) { + stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + } else { + stack.setWindowingMode(WINDOWING_MODE_FREEFORM); + } } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 6dc73bbb80cb..19ff43822923 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -1879,6 +1879,9 @@ public class AppTransition implements Dump { mNextAppTransitionAnimationsSpecsFuture = specsFuture; mNextAppTransitionScaleUp = scaleUp; mNextAppTransitionFutureCallback = callback; + if (isReady()) { + fetchAppTransitionSpecsFromFuture(); + } } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 97062a6065e2..5c528c7f6047 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -568,7 +568,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { final long token = proto.start(fieldId); mRequestedOverrideConfiguration.writeToProto(proto, OVERRIDE_CONFIGURATION, - logLevel != WindowTraceLogLevel.CRITICAL); + logLevel == WindowTraceLogLevel.CRITICAL); if (logLevel == WindowTraceLogLevel.ALL) { mFullConfiguration.writeToProto(proto, FULL_CONFIGURATION, false /* critical */); mMergedOverrideConfiguration.writeToProto(proto, MERGED_OVERRIDE_CONFIGURATION, diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5cfc20b6339f..4795555e8ed2 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1500,8 +1500,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; final int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / mBaseDisplayDensity; - mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayPolicy.configure(width, height, shortSizeDp); + mDisplayRotation.configure(width, height, shortSizeDp, longSizeDp); mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2ee30ac5c8ff..91d573defc16 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2617,9 +2617,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int width = fullWidth; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in landscape mode we place - // the navigation bar to the side. - if (navigationBarCanMove() && fullWidth > fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { width -= getNavigationBarWidth(rotation, uiMode); } } @@ -2646,9 +2645,8 @@ public class DisplayPolicy { DisplayCutout displayCutout) { int height = fullHeight; if (hasNavigationBar()) { - // For a basic navigation bar, when we are in portrait mode we place - // the navigation bar to the bottom. - if (!navigationBarCanMove() || fullWidth < fullHeight) { + final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); + if (navBarPosition == NAV_BAR_BOTTOM) { height -= getNavigationBarHeight(rotation, uiMode); } } diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 5f341ee8002c..543f19655350 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; +import android.view.DisplayCutout; import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; @@ -70,6 +71,8 @@ public class DisplayRotation { private final int mDeskDockRotation; private final int mUndockedHdmiRotation; + private final float mCloseToSquareMaxAspectRatio; + private OrientationListener mOrientationListener; private StatusBarManagerInternal mStatusBarManagerInternal; private SettingsObserver mSettingsObserver; @@ -132,6 +135,9 @@ public class DisplayRotation { mUndockedHdmiRotation = readRotation( com.android.internal.R.integer.config_undockedHdmiRotation); + mCloseToSquareMaxAspectRatio = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio); + if (isDefaultDisplay) { final Handler uiHandler = UiThread.getHandler(); mOrientationListener = new OrientationListener(mContext, uiHandler); @@ -212,10 +218,12 @@ public class DisplayRotation { // so if the orientation is forced, we need to respect that no matter what. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + final boolean isCloseToSquare = + isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height); final boolean forceDefaultOrientationInRes = res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); final boolean forceDefaultOrienation = - ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv) + ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare) && forceDefaultOrientationInRes // For debug purposes the next line turns this feature off with: // $ adb shell setprop config.override_forced_orient true @@ -227,6 +235,18 @@ public class DisplayRotation { setFixedToUserRotation(forceDefaultOrienation); } + private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) { + final DisplayCutout displayCutout = + mDisplayContent.calculateDisplayCutoutForRotation(rotation).getDisplayCutout(); + final int uiMode = mService.mPolicy.getUiMode(); + final int w = mDisplayPolicy.getNonDecorDisplayWidth( + width, height, rotation, uiMode, displayCutout); + final int h = mDisplayPolicy.getNonDecorDisplayHeight( + width, height, rotation, uiMode, displayCutout); + final float aspectRatio = Math.max(w, h) / (float) Math.min(w, h); + return aspectRatio <= mCloseToSquareMaxAspectRatio; + } + void setRotation(int rotation) { if (mOrientationListener != null) { mOrientationListener.setCurrentRotation(rotation); diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index ea65dd99077a..1d76a71aaea1 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -396,6 +396,10 @@ public class DockedStackDividerController { if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight) || mAdjustedForDivider != adjustedForDivider) { if (animate && !mAnimatingForMinimizedDockedStack) { + // Notify SystemUI to set the target docked stack size according current docked + // state without animation when calling startImeAdjustAnimation. + notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */, + isHomeStackResizable()); startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); } else { // Animation might be delayed, so only notify if we don't run an animation. @@ -889,7 +893,10 @@ public class DockedStackDividerController { } if (mAnimatingForMinimizedDockedStack) { return animateForMinimizedDockedStack(now); - } else if (mAnimatingForIme) { + } else if (mAnimatingForIme && !mDisplayContent.mAppTransition.isRunning()) { + // To prevent task stack resize animation may flicking when playing app transition + // animation & IME window enter animation in parallel, make sure app transition is done + // and then start to animate for IME. return animateForIme(now); } return false; diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java index 5c91d9e6fff2..698835772df5 100644 --- a/services/core/java/com/android/server/wm/RootActivityContainer.java +++ b/services/core/java/com/android/server/wm/RootActivityContainer.java @@ -1126,6 +1126,13 @@ class RootActivityContainer extends ConfigurationContainer if (!stack.isFocusableAndVisible() || topRunningActivity == null) { continue; } + if (stack == targetStack) { + // Simply update the result for targetStack because the targetStack had + // already resumed in above. We don't want to resume it again, especially in + // some cases, it would cause a second launch failure if app process was dead. + resumedOnDisplay |= result; + continue; + } if (topRunningActivity.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront operation. stack.executeAppTransition(targetOptions); diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 59d7560fc5b0..9d3112fec0f2 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -2134,7 +2134,7 @@ class TaskRecord extends ConfigurationContainer { if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); inOutConfig.screenHeightDp = insideParentBounds - ? Math.min(overrideScreenHeightDp, parentConfig.screenWidthDp) + ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) : overrideScreenHeightDp; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 7b742fd2e0f3..b91519903eb8 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1163,6 +1163,14 @@ public class TaskStack extends WindowContainer<Task> implements } private boolean adjustForIME(final WindowState imeWin) { + // To prevent task stack resize animation may flicking when playing app transition + // animation & IME window enter animation in parallel, we need to make sure app + // transition is done and then adjust task size for IME, skip the new adjusted frame when + // app transition is still running. + if (getDisplayContent().mAppTransition.isRunning()) { + return false; + } + final int dockedSide = getDockSide(); final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; if (imeWin == null || !dockedTopOrBottom) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c747c6d95fcd..7beee0e33355 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; +import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW; @@ -446,7 +447,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean mLimitedAlphaCompositing; final int mMaxUiWidth; - final WindowManagerPolicy mPolicy; + @VisibleForTesting + WindowManagerPolicy mPolicy; final IActivityManager mActivityManager; // TODO: Probably not needed once activities are fully in WM. @@ -3758,6 +3760,41 @@ public class WindowManagerService extends IWindowManager.Stub mPolicy.unregisterDisplayFoldListener(listener); } + /** + * Overrides the folded area. + * + * @param area the overriding folded area or an empty {@code Rect} to clear the override. + */ + void setOverrideFoldedArea(@NonNull Rect area) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + mPolicy.setOverrideFoldedArea(area); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + /** + * Get the display folded area. + */ + @NonNull Rect getFoldedArea() { + long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + return mPolicy.getFoldedArea(); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + @Override public int getPreferredOptionsPanelGravity(int displayId) { synchronized (mGlobalLock) { @@ -4263,9 +4300,12 @@ public class WindowManagerService extends IWindowManager.Stub if (mMaxUiWidth > 0) { mRoot.forAllDisplays(displayContent -> displayContent.setMaxUiWidth(mMaxUiWidth)); } - applyForcedPropertiesForDefaultDisplay(); + final boolean changed = applyForcedPropertiesForDefaultDisplay(); mAnimator.ready(); mDisplayReady = true; + if (changed) { + reconfigureDisplayLocked(getDefaultDisplayContentLocked()); + } mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN); } @@ -4822,11 +4862,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplaySize(int displayId, int width, int height) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4844,11 +4882,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplayScalingMode(int displayId, int mode) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4865,7 +4901,8 @@ public class WindowManagerService extends IWindowManager.Stub } /** The global settings only apply to default display. */ - private void applyForcedPropertiesForDefaultDisplay() { + private boolean applyForcedPropertiesForDefaultDisplay() { + boolean changed = false; final DisplayContent displayContent = getDefaultDisplayContentLocked(); // Display size. String sizeStr = Settings.Global.getString(mContext.getContentResolver(), @@ -4885,6 +4922,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG_WM, "FORCED DISPLAY SIZE: " + width + "x" + height); displayContent.updateBaseDisplayMetrics(width, height, displayContent.mBaseDisplayDensity); + changed = true; } } catch (NumberFormatException ex) { } @@ -4893,26 +4931,27 @@ public class WindowManagerService extends IWindowManager.Stub // Display density. final int density = getForcedDisplayDensityForUserLocked(mCurrentUserId); - if (density != 0) { + if (density != 0 && density != displayContent.mBaseDisplayDensity) { displayContent.mBaseDisplayDensity = density; + changed = true; } // Display scaling mode. int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DISPLAY_SCALING_FORCE, 0); - if (mode != 0) { + if (displayContent.mDisplayScalingDisabled != (mode != 0)) { Slog.i(TAG_WM, "FORCED DISPLAY SCALING DISABLED"); displayContent.mDisplayScalingDisabled = true; + changed = true; } + return changed; } @Override public void clearForcedDisplaySize(int displayId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); @@ -4953,11 +4992,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForcedDisplayDensityForUser(int displayId, int density, int userId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), @@ -4978,11 +5015,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void clearForcedDisplayDensityForUser(int displayId, int userId) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), @@ -5047,11 +5082,9 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setOverscan(int displayId, int left, int top, int right, int bottom) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) != - PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Must hold permission " + - android.Manifest.permission.WRITE_SECURE_SETTINGS); + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); } final long ident = Binder.clearCallingIdentity(); try { @@ -5081,11 +5114,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void startWindowTrace(){ - try { - mWindowTracing.startTrace(null /* printwriter */); - } catch (IOException e) { - throw new RuntimeException(e); - } + mWindowTracing.startTrace(null /* printwriter */); } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 83e3c71cbee3..d13ee459c115 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -62,6 +62,8 @@ public class WindowManagerShellCommand extends ShellCommand { return runDisplaySize(pw); case "density": return runDisplayDensity(pw); + case "folded-area": + return runDisplayFoldedArea(pw); case "overscan": return runDisplayOverscan(pw); case "scaling": @@ -207,6 +209,40 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private void printFoldedArea(PrintWriter pw) { + final Rect foldedArea = mInternal.getFoldedArea(); + if (foldedArea.isEmpty()) { + pw.println("Folded area: none"); + } else { + pw.println("Folded area: " + foldedArea.left + "," + foldedArea.top + "," + + foldedArea.right + "," + foldedArea.bottom); + } + } + + private int runDisplayFoldedArea(PrintWriter pw) { + final String areaStr = getNextArg(); + final Rect rect = new Rect(); + if (areaStr == null) { + printFoldedArea(pw); + return 0; + } else if ("reset".equals(areaStr)) { + rect.setEmpty(); + } else { + final Pattern flattenedPattern = Pattern.compile( + "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)"); + final Matcher matcher = flattenedPattern.matcher(areaStr); + if (!matcher.matches()) { + getErrPrintWriter().println("Error: area should be LEFT,TOP,RIGHT,BOTTOM"); + return -1; + } + rect.set(Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4))); + } + + mInternal.setOverrideFoldedArea(rect); + return 0; + } + private int runDisplayOverscan(PrintWriter pw) throws RemoteException { String overscanStr = getNextArgRequired(); Rect rect = new Rect(); @@ -335,6 +371,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" width and height in pixels unless suffixed with 'dp'."); pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]"); pw.println(" Return or override display density."); + pw.println(" folded-area [reset|LEFT,TOP,RIGHT,BOTTOM]"); + pw.println(" Return or override folded area."); pw.println(" overscan [reset|LEFT,TOP,RIGHT,BOTTOM] [-d DISPLAY ID]"); pw.println(" Set overscan area for display."); pw.println(" scaling [off|auto] [-d DISPLAY_ID]"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 3430987f4736..21a557e6809e 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2164,9 +2164,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // A modal window uses the whole compatibility bounds. flags |= FLAG_NOT_TOUCH_MODAL; mTmpRect.set(mAppToken.getResolvedOverrideBounds()); - // TODO(b/112288258): Remove the forced offset when the override bounds always - // starts from zero (See {@link ActivityRecord#resolveOverrideConfiguration}). - mTmpRect.offsetTo(0, 0); } else { // Non-modal uses the application based frame. mTmpRect.set(mWindowFrames.mCompatFrame); diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java index e4461ea90c91..2ce6e6c1d049 100644 --- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java +++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java @@ -20,7 +20,6 @@ import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H; import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L; -import android.os.Trace; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -36,24 +35,30 @@ import java.util.Queue; /** * Buffer used for window tracing. */ -abstract class WindowTraceBuffer { +class WindowTraceBuffer { private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - final Object mBufferLock = new Object(); - final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); - final File mTraceFile; - int mBufferSize; - private final int mBufferCapacity; + private final Object mBufferLock = new Object(); - WindowTraceBuffer(int size, File traceFile) throws IOException { - mBufferCapacity = size; - mTraceFile = traceFile; + private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>(); + private int mBufferUsedSize; + private int mBufferCapacity; - initTraceFile(); + WindowTraceBuffer(int bufferCapacity) { + mBufferCapacity = bufferCapacity; + resetBuffer(); } int getAvailableSpace() { - return mBufferCapacity - mBufferSize; + return mBufferCapacity - mBufferUsedSize; + } + + int size() { + return mBuffer.size(); + } + + void setCapacity(int capacity) { + mBufferCapacity = capacity; } /** @@ -70,42 +75,37 @@ abstract class WindowTraceBuffer { + mBufferCapacity + " Object size: " + protoLength); } synchronized (mBufferLock) { - boolean canAdd = canAdd(protoLength); - if (canAdd) { - mBuffer.add(proto); - mBufferSize += protoLength; - } + discardOldest(protoLength); + mBuffer.add(proto); + mBufferUsedSize += protoLength; mBufferLock.notify(); } } - /** - * Stops the buffer execution and flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} - */ - void dump() throws IOException, InterruptedException { - try { - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile"); - writeTraceToFile(); - } finally { - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - } - } - - @VisibleForTesting boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(p.getBytes(), other)); } - private void initTraceFile() throws IOException { - mTraceFile.delete(); - try (OutputStream os = new FileOutputStream(mTraceFile)) { - mTraceFile.setReadable(true, false); - ProtoOutputStream proto = new ProtoOutputStream(os); - proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); - proto.flush(); + /** + * Writes the trace buffer to disk. + */ + void writeTraceToFile(File traceFile) throws IOException { + synchronized (mBufferLock) { + traceFile.delete(); + traceFile.setReadable(true, false); + try (OutputStream os = new FileOutputStream(traceFile)) { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + os.write(proto.getBytes()); + while (!mBuffer.isEmpty()) { + proto = mBuffer.poll(); + mBufferUsedSize -= proto.getRawSize(); + byte[] protoBytes = proto.getBytes(); + os.write(protoBytes); + } + os.flush(); + } } } @@ -114,59 +114,48 @@ abstract class WindowTraceBuffer { * smaller than the overall buffer size. * * @param protoLength byte array representation of the Proto object to add - * @return {@code true} if the element can be added to the buffer or not - */ - abstract boolean canAdd(int protoLength); - - /** - * Flush all buffer content to the disk. - * - * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile} */ - abstract void writeTraceToFile() throws IOException, InterruptedException; - - /** - * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for - * continuous mode or a {@link WindowTraceQueueBuffer} otherwise - */ - static class Builder { - private boolean mContinuous; - private File mTraceFile; - private int mBufferCapacity; - - Builder setContinuousMode(boolean continuous) { - mContinuous = continuous; - return this; - } + private void discardOldest(int protoLength) { + long availableSpace = getAvailableSpace(); - Builder setTraceFile(File traceFile) { - mTraceFile = traceFile; - return this; - } + while (availableSpace < protoLength) { - Builder setBufferCapacity(int size) { - mBufferCapacity = size; - return this; + ProtoOutputStream item = mBuffer.poll(); + if (item == null) { + throw new IllegalStateException("No element to discard from buffer"); + } + mBufferUsedSize -= item.getRawSize(); + availableSpace = getAvailableSpace(); } + } - File getFile() { - return mTraceFile; + /** + * Removes all elements form the buffer + */ + void resetBuffer() { + synchronized (mBufferLock) { + mBuffer.clear(); + mBufferUsedSize = 0; } + } - WindowTraceBuffer build() throws IOException { - if (mBufferCapacity <= 0) { - throw new IllegalStateException("Buffer capacity must be greater than 0."); - } - - if (mTraceFile == null) { - throw new IllegalArgumentException("A valid trace file must be specified."); - } + @VisibleForTesting + int getBufferSize() { + return mBufferUsedSize; + } - if (mContinuous) { - return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile); - } else { - return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile); - } + String getStatus() { + synchronized (mBufferLock) { + return "Buffer size: " + + mBufferCapacity + + " bytes" + + "\n" + + "Buffer usage: " + + mBufferUsedSize + + " bytes" + + "\n" + + "Elements in the buffer: " + + mBuffer.size(); } } } diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java deleted file mode 100644 index 5888b7a799cf..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.os.Build.IS_USER; - -import android.util.Log; -import android.util.proto.ProtoOutputStream; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first - * {@code #size size} bytes of window trace elements. - * Once the buffer is full it will no longer accepts new elements. - */ -class WindowTraceQueueBuffer extends WindowTraceBuffer { - private static final String TAG = "WindowTracing"; - - private Thread mConsumerThread; - private boolean mCancel; - - @VisibleForTesting - WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread) - throws IOException { - super(size, traceFile); - if (startConsumerThread) { - initializeConsumerThread(); - } - } - - WindowTraceQueueBuffer(int size, File traceFile) throws IOException { - this(size, traceFile, !IS_USER); - } - - private void initializeConsumerThread() { - mCancel = false; - mConsumerThread = new Thread(() -> { - try { - loop(); - } catch (InterruptedException e) { - Log.i(TAG, "Interrupting trace consumer thread"); - } catch (IOException e) { - Log.e(TAG, "Failed to execute trace consumer thread", e); - } - }, "window_tracing"); - mConsumerThread.start(); - } - - private void loop() throws IOException, InterruptedException { - while (!mCancel) { - ProtoOutputStream proto; - synchronized (mBufferLock) { - mBufferLock.wait(); - proto = mBuffer.poll(); - if (proto != null) { - mBufferSize -= proto.getRawSize(); - } - } - if (proto != null) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - return availableSpace >= protoLength; - } - - @Override - void writeTraceToFile() throws InterruptedException { - synchronized (mBufferLock) { - mCancel = true; - mBufferLock.notify(); - } - if (mConsumerThread != null) { - mConsumerThread.join(); - mConsumerThread = null; - } - } -} diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java deleted file mode 100644 index 77d30be816bc..000000000000 --- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import android.util.proto.ProtoOutputStream; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * A ring buffer to store the {@code #size size} bytes of window trace data. - * The buffer operates on a trace entry level, that is, if the new trace data is larger than the - * available buffer space, the buffer will discard as many full trace entries as necessary to fit - * the new trace. - */ -class WindowTraceRingBuffer extends WindowTraceBuffer { - WindowTraceRingBuffer(int size, File traceFile) throws IOException { - super(size, traceFile); - } - - @Override - boolean canAdd(int protoLength) { - long availableSpace = getAvailableSpace(); - - while (availableSpace < protoLength) { - discardOldest(); - availableSpace = getAvailableSpace(); - } - - return true; - } - - @Override - void writeTraceToFile() throws IOException { - synchronized (mBufferLock) { - try (OutputStream os = new FileOutputStream(mTraceFile, true)) { - while (!mBuffer.isEmpty()) { - ProtoOutputStream proto = mBuffer.poll(); - mBufferSize -= proto.getRawSize(); - byte[] protoBytes = proto.getBytes(); - os.write(protoBytes); - } - } - } - } - - private void discardOldest() { - ProtoOutputStream item = mBuffer.poll(); - if (item == null) { - throw new IllegalStateException("No element to discard from buffer"); - } - mBufferSize -= item.getRawSize(); - } -} diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index abc474d756b7..0ce215c88dad 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -31,8 +31,6 @@ import android.util.Log; import android.util.proto.ProtoOutputStream; import android.view.Choreographer; -import com.android.internal.annotations.VisibleForTesting; - import java.io.File; import java.io.IOException; import java.io.PrintWriter; @@ -47,139 +45,191 @@ class WindowTracing { * Maximum buffer size, currently defined as 512 KB * Size was experimentally defined to fit between 100 to 150 elements. */ - private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024; + private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024; + private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024; + private static final int BUFFER_CAPACITY_ALL = 4096 * 1024; + private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb"; private static final String TAG = "WindowTracing"; private final WindowManagerService mService; private final Choreographer mChoreographer; private final WindowManagerGlobalLock mGlobalLock; - private final Object mLock = new Object(); - private final WindowTraceBuffer.Builder mBufferBuilder; - - private WindowTraceBuffer mTraceBuffer; + private final Object mEnabledLock = new Object(); + private final File mTraceFile; + private final WindowTraceBuffer mBuffer; + private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> + log("onFrame" /* where */); - private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM; - private boolean mContinuousMode; + private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM; + private boolean mLogOnFrame = false; private boolean mEnabled; private volatile boolean mEnabledLockFree; private boolean mScheduled; - private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) -> - log("onFrame" /* where */); - private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) { - this(file, service, choreographer, service.mGlobalLock); + static WindowTracing createDefaultAndStartLooper(WindowManagerService service, + Choreographer choreographer) { + File file = new File(TRACE_FILENAME); + return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM); } - @VisibleForTesting - WindowTracing(File file, WindowManagerService service, Choreographer choreographer, - WindowManagerGlobalLock globalLock) { - mBufferBuilder = new WindowTraceBuffer.Builder() - .setTraceFile(file) - .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE); + private WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + int bufferCapacity) { + this(file, service, choreographer, service.mGlobalLock, bufferCapacity); + } + WindowTracing(File file, WindowManagerService service, Choreographer choreographer, + WindowManagerGlobalLock globalLock, int bufferCapacity) { mChoreographer = choreographer; mService = service; mGlobalLock = globalLock; + mTraceFile = file; + mBuffer = new WindowTraceBuffer(bufferCapacity); + setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */); } - void startTrace(@Nullable PrintWriter pw) throws IOException { + void startTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + "."); - if (mTraceBuffer != null) { - writeTraceToFileLocked(); - } - mTraceBuffer = mBufferBuilder - .setContinuousMode(mContinuousMode) - .build(); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Start tracing to " + mTraceFile + "."); + mBuffer.resetBuffer(); mEnabled = mEnabledLockFree = true; } } - private void logAndPrintln(@Nullable PrintWriter pw, String msg) { - Log.i(TAG, msg); - if (pw != null) { - pw.println(msg); - pw.flush(); - } - } - void stopTrace(@Nullable PrintWriter pw) { if (IS_USER) { logAndPrintln(pw, "Error: Tracing is not supported on user builds."); return; } - synchronized (mLock) { - logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile() - + ". Waiting for traces to flush."); + synchronized (mEnabledLock) { + logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush."); mEnabled = mEnabledLockFree = false; - synchronized (mLock) { - if (mEnabled) { - logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); - throw new IllegalStateException("tracing enabled while waiting for flush."); - } - writeTraceToFileLocked(); - mTraceBuffer = null; + if (mEnabled) { + logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush."); + throw new IllegalStateException("tracing enabled while waiting for flush."); } - logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + "."); + writeTraceToFileLocked(); + logAndPrintln(pw, "Trace written to " + mTraceFile + "."); } } - @VisibleForTesting - void setContinuousMode(boolean continuous, PrintWriter pw) { - logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous); + private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log level to " + logLevel); + mLogLevel = logLevel; - if (mEnabled) { - logAndPrintln(pw, "Trace is currently active, change will take effect once the " - + "trace is restarted."); + switch (logLevel) { + case WindowTraceLogLevel.ALL: { + setBufferCapacity(BUFFER_CAPACITY_ALL, pw); + break; + } + case WindowTraceLogLevel.TRIM: { + setBufferCapacity(BUFFER_CAPACITY_TRIM, pw); + break; + } + case WindowTraceLogLevel.CRITICAL: { + setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw); + break; + } } - mContinuousMode = continuous; - mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL : - WindowTraceLogLevel.TRIM; } - boolean isEnabled() { - return mEnabledLockFree; + private void setLogFrequency(boolean onFrame, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing log frequency to " + + ((onFrame) ? "frame" : "transaction")); + mLogOnFrame = onFrame; } - static WindowTracing createDefaultAndStartLooper(WindowManagerService service, - Choreographer choreographer) { - File file = new File("/data/misc/wmtrace/wm_trace.pb"); - return new WindowTracing(file, service, choreographer); + private void setBufferCapacity(int capacity, PrintWriter pw) { + logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes"); + mBuffer.setCapacity(capacity); + } + + boolean isEnabled() { + return mEnabledLockFree; } int onShellCommand(ShellCommand shell) { PrintWriter pw = shell.getOutPrintWriter(); - try { - String cmd = shell.getNextArgRequired(); - switch (cmd) { - case "start": - startTrace(pw); - return 0; - case "stop": - stopTrace(pw); - return 0; - case "continuous": - setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw); - return 0; - default: - pw.println("Unknown command: " + cmd); - return -1; - } - } catch (IOException e) { - logAndPrintln(pw, e.toString()); - throw new RuntimeException(e); + String cmd = shell.getNextArgRequired(); + switch (cmd) { + case "start": + startTrace(pw); + return 0; + case "stop": + stopTrace(pw); + return 0; + case "status": + logAndPrintln(pw, getStatus()); + return 0; + case "frame": + setLogFrequency(true /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "transaction": + setLogFrequency(false /* onFrame */, pw); + mBuffer.resetBuffer(); + return 0; + case "level": + String logLevelStr = shell.getNextArgRequired().toLowerCase(); + switch (logLevelStr) { + case "all": { + setLogLevel(WindowTraceLogLevel.ALL, pw); + break; + } + case "trim": { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + case "critical": { + setLogLevel(WindowTraceLogLevel.CRITICAL, pw); + break; + } + default: { + setLogLevel(WindowTraceLogLevel.TRIM, pw); + break; + } + } + mBuffer.resetBuffer(); + return 0; + case "size": + setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw); + mBuffer.resetBuffer(); + return 0; + default: + pw.println("Unknown command: " + cmd); + pw.println("Window manager trace options:"); + pw.println(" start: Start logging"); + pw.println(" stop: Stop logging"); + pw.println(" frame: Log trace once per frame"); + pw.println(" transaction: Log each transaction"); + pw.println(" size: Set the maximum log size (in KB)"); + pw.println(" level [lvl]: Set the log level between"); + pw.println(" lvl may be one of:"); + pw.println(" critical: Only visible windows with reduced information"); + pw.println(" trim: All windows with reduced"); + pw.println(" all: All window and information"); + return -1; } } + private String getStatus() { + return "Status: " + + ((isEnabled()) ? "Enabled" : "Disabled") + + "\n" + + "Log level: " + + mLogLevel + + "\n" + + mBuffer.getStatus(); + } + /** * If tracing is enabled, log the current state or schedule the next frame to be logged, - * according to {@link #mContinuousMode}. + * according to {@link #mLogOnFrame}. * * @param where Logging point descriptor */ @@ -188,7 +238,7 @@ class WindowTracing { return; } - if (mContinuousMode) { + if (mLogOnFrame) { schedule(); } else { log(where); @@ -215,25 +265,24 @@ class WindowTracing { private void log(String where) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked"); try { - synchronized (mGlobalLock) { - ProtoOutputStream os = new ProtoOutputStream(); - long tokenOuter = os.start(ENTRY); - os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); - os.write(WHERE, where); + ProtoOutputStream os = new ProtoOutputStream(); + long tokenOuter = os.start(ENTRY); + os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos()); + os.write(WHERE, where); + long tokenInner = os.start(WINDOW_MANAGER_SERVICE); + synchronized (mGlobalLock) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked"); try { - long tokenInner = os.start(WINDOW_MANAGER_SERVICE); - mService.writeToProtoLocked(os, mWindowTraceLogLevel); - os.end(tokenInner); + mService.writeToProtoLocked(os, mLogLevel); } finally { Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } - os.end(tokenOuter); - mTraceBuffer.add(os); - - mScheduled = false; } + os.end(tokenInner); + os.end(tokenOuter); + mBuffer.add(os); + mScheduled = false; } catch (Exception e) { Log.wtf(TAG, "Exception while tracing state", e); } finally { @@ -242,31 +291,37 @@ class WindowTracing { } /** - * Writes the trace buffer to disk. This method has no internal synchronization and should be - * externally synchronized + * Writes the trace buffer to new file for the bugreport. + * + * This method is synchronized with {@code #startTrace(PrintWriter)} and + * {@link #stopTrace(PrintWriter)}. */ - private void writeTraceToFileLocked() { - if (mTraceBuffer == null) { - return; + void writeTraceToFile() { + synchronized (mEnabledLock) { + writeTraceToFileLocked(); } + } - try { - mTraceBuffer.dump(); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } catch (InterruptedException e) { - Log.e(TAG, "Unable to interrupt window tracing file write thread", e); + private void logAndPrintln(@Nullable PrintWriter pw, String msg) { + Log.i(TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); } } /** - * Writes the trace buffer to disk and clones it into a new file for the bugreport. - * This method is synchronized with {@code #startTrace(PrintWriter)} and - * {@link #stopTrace(PrintWriter)}. + * Writes the trace buffer to disk. This method has no internal synchronization and should be + * externally synchronized */ - void writeTraceToFile() { - synchronized (mLock) { - writeTraceToFileLocked(); + private void writeTraceToFileLocked() { + try { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked"); + mBuffer.writeTraceToFile(mTraceFile); + } catch (IOException e) { + Log.e(TAG, "Unable to write buffer to file", e); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } -} +}
\ No newline at end of file diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 5c19ad33617c..9cbb58d35b9f 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "BatteryStatsService" //#define LOG_NDEBUG 0 +#include <climits> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -28,6 +29,7 @@ #include <sys/types.h> #include <unistd.h> #include <unordered_map> +#include <utility> #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> @@ -87,6 +89,15 @@ std::function<void(JNIEnv*, jobject)> gGetLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetPlatformLowPowerStatsImpl = {}; std::function<jint(JNIEnv*, jobject)> gGetSubsystemLowPowerStatsImpl = {}; +// Cellular/Wifi power monitor rail information +static jmethodID jupdateRailData = NULL; +static jmethodID jsetRailStatsAvailability = NULL; + +std::function<void(JNIEnv*, jobject)> gGetRailEnergyPowerStatsImpl = {}; + +std::unordered_map<uint32_t, std::pair<std::string, std::string>> gPowerStatsHalRailNames = {}; +static bool power_monitor_available = false; + // The caller must be holding gPowerHalMutex. static void deinitPowerStatsLocked() { gPowerStatsHalV1_0 = nullptr; @@ -258,6 +269,7 @@ static bool initializePowerStats() { gPowerStatsHalStateNames.clear(); gPowerStatsHalPlatformIds.clear(); gPowerStatsHalSubsystemIds.clear(); + gPowerStatsHalRailNames.clear(); Return<void> ret; ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) { @@ -301,6 +313,27 @@ static bool initializePowerStats() { return false; } + // Get Power monitor rails available + ret = gPowerStatsHalV1_0->getRailInfo([](auto rails, auto status) { + if (status != Status::SUCCESS) { + ALOGW("Rail information is not available"); + power_monitor_available = false; + return; + } + + // Fill out rail names/subsystems into gPowerStatsHalRailNames + for (auto rail : rails) { + gPowerStatsHalRailNames.emplace(rail.index, + std::make_pair(rail.railName, rail.subsysName)); + } + if (!gPowerStatsHalRailNames.empty()) { + power_monitor_available = true; + } + }); + if (!checkResultLocked(ret, __func__)) { + return false; + } + return (!gPowerStatsHalEntityNames.empty()) && (!gPowerStatsHalStateNames.empty()); } @@ -517,6 +550,50 @@ static jint getPowerStatsHalSubsystemData(JNIEnv* env, jobject outBuf) { return total_added; } +static void getPowerStatsHalRailEnergyData(JNIEnv* env, jobject jrailStats) { + using android::hardware::power::stats::V1_0::Status; + using android::hardware::power::stats::V1_0::EnergyData; + + if (!getPowerStatsHalLocked()) { + ALOGE("failed to get power stats"); + return; + } + + if (!power_monitor_available) { + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGW("Rail energy data is not available"); + return; + } + + // Get power rail energySinceBoot data + Return<void> ret = gPowerStatsHalV1_0->getEnergyData({}, + [&env, &jrailStats](auto energyData, auto status) { + if (status == Status::NOT_SUPPORTED) { + ALOGW("getEnergyData is not supported"); + return; + } + + for (auto data : energyData) { + if (!(data.timestamp > LLONG_MAX || data.energy > LLONG_MAX)) { + env->CallVoidMethod(jrailStats, + jupdateRailData, + data.index, + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).first.c_str()), + env->NewStringUTF( + gPowerStatsHalRailNames.at(data.index).second.c_str()), + data.timestamp, + data.energy); + } else { + ALOGE("Java long overflow seen. Rail index %d not updated", data.index); + } + } + }); + if (!checkResultLocked(ret, __func__)) { + ALOGE("getEnergyData failed"); + } +} + // The caller must be holding powerHalMutex. static void getPowerHalLowPowerData(JNIEnv* env, jobject jrpmStats) { sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0(); @@ -761,11 +838,13 @@ static void setUpPowerStatsLocked() { gGetLowPowerStatsImpl = getPowerStatsHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerStatsHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerStatsHalSubsystemData; + gGetRailEnergyPowerStatsImpl = getPowerStatsHalRailEnergyData; } else if (android::hardware::power::V1_0::IPower::getService() != nullptr) { ALOGI("Using power HAL"); gGetLowPowerStatsImpl = getPowerHalLowPowerData; gGetPlatformLowPowerStatsImpl = getPowerHalPlatformData; gGetSubsystemLowPowerStatsImpl = getPowerHalSubsystemData; + gGetRailEnergyPowerStatsImpl = NULL; } } @@ -835,11 +914,44 @@ static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject return -1; } +static void getRailEnergyPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrailStats) { + if (jrailStats == NULL) { + jniThrowException(env, "java/lang/NullPointerException", + "The railstats jni input jobject jrailStats is null."); + return; + } + if (jupdateRailData == NULL) { + ALOGE("A railstats jni jmethodID is null."); + return; + } + + std::lock_guard<std::mutex> lock(gPowerHalMutex); + + if (!gGetRailEnergyPowerStatsImpl) { + setUpPowerStatsLocked(); + } + + if (gGetRailEnergyPowerStatsImpl) { + gGetRailEnergyPowerStatsImpl(env, jrailStats); + return; + } + + if (jsetRailStatsAvailability == NULL) { + ALOGE("setRailStatsAvailability jni jmethodID is null."); + return; + } + env->CallVoidMethod(jrailStats, jsetRailStatsAvailability, false); + ALOGE("Unable to load Power.Stats.HAL. Setting rail availability to false"); + return; +} + static const JNINativeMethod method_table[] = { { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats }, { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats }, + { "getRailEnergyPowerStats", "(Lcom/android/internal/os/RailStats;)V", + (void*)getRailEnergyPowerStats }, }; int register_android_server_BatteryStatsService(JNIEnv *env) @@ -850,8 +962,9 @@ int register_android_server_BatteryStatsService(JNIEnv *env) env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState"); jclass clsPowerStateSubsystem = env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem"); + jclass clsRailStats = env->FindClass("com/android/internal/os/RailStats"); if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL - || clsPowerStateSubsystem == NULL) { + || clsPowerStateSubsystem == NULL || clsRailStats == NULL) { ALOGE("A rpmstats jni jclass is null."); } else { jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState", @@ -862,6 +975,10 @@ int register_android_server_BatteryStatsService(JNIEnv *env) "(Ljava/lang/String;JI)V"); jputState = env->GetMethodID(clsPowerStateSubsystem, "putState", "(Ljava/lang/String;JI)V"); + jupdateRailData = env->GetMethodID(clsRailStats, "updateRailData", + "(JLjava/lang/String;Ljava/lang/String;JJ)V"); + jsetRailStatsAvailability = env->GetMethodID(clsRailStats, "setRailStatsAvailability", + "(Z)V"); } return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index 9be728bac532..ec7a78beb122 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -295,6 +295,12 @@ static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint feature } } +static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) { + bool retval = false; + getSuspendControl()->forceSuspend(&retval); + return retval; +} + // ---------------------------------------------------------------------------- static const JNINativeMethod gPowerManagerServiceMethods[] = { @@ -303,6 +309,8 @@ static const JNINativeMethod gPowerManagerServiceMethods[] = { (void*) nativeInit }, { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", (void*) nativeAcquireSuspendBlocker }, + { "nativeForceSuspend", "()Z", + (void*) nativeForceSuspend }, { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", (void*) nativeReleaseSuspendBlocker }, { "nativeSetInteractive", "(Z)V", diff --git a/services/devicepolicy/TEST_MAPPING b/services/devicepolicy/TEST_MAPPING new file mode 100644 index 000000000000..ab85a6873cf6 --- /dev/null +++ b/services/devicepolicy/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "CtsDevicePolicyManagerTestCases" + } + ] +} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9523202cbb0e..ae48dad7b8f9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5181,7 +5181,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); final int userHandle = mInjector.userHandleGetCallingUserId(); synchronized (getLockObject()) { - ActiveAdmin ap = getActiveAdminForCallerLocked( + final ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; @@ -8388,7 +8388,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return true; } - Log.w(LOG_TAG, String.format("Package if %s (uid=%d, pid=%d) cannot access Device IDs", + Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs", packageName, uid, pid)); return false; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a6017f2c1e86..aae159c2edcb 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -36,6 +36,7 @@ import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; +import android.net.NetworkStackClient; import android.os.BaseBundle; import android.os.Binder; import android.os.Build; @@ -1350,9 +1351,7 @@ public final class SystemServer { traceBeginAndSlog("StartNetworkStack"); try { - final android.net.NetworkStack networkStack = - context.getSystemService(android.net.NetworkStack.class); - networkStack.start(context); + NetworkStackClient.getInstance().start(context); } catch (Throwable e) { reportWtf("starting Network Stack", e); } diff --git a/services/net/Android.bp b/services/net/Android.bp index 638ec95ec544..8ad4d7679107 100644 --- a/services/net/Android.bp +++ b/services/net/Android.bp @@ -1,6 +1,10 @@ java_library_static { name: "services.net", srcs: ["java/**/*.java"], + static_libs: [ + "netd_aidl_interface-java", + "networkstack-aidl-interfaces-java", + ] } filegroup { diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java new file mode 100644 index 000000000000..1eb7b98d801a --- /dev/null +++ b/services/net/java/android/net/NetworkStackClient.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.net; + +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.net.dhcp.DhcpServingParamsParcel; +import android.net.dhcp.IDhcpServerCallbacks; +import android.net.ip.IIpClientCallbacks; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +/** + * Service used to communicate with the network stack, which is running in a separate module. + * @hide + */ +public class NetworkStackClient { + private static final String TAG = NetworkStackClient.class.getSimpleName(); + + private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; + + private static NetworkStackClient sInstance; + + @NonNull + @GuardedBy("mPendingNetStackRequests") + private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>(); + @Nullable + @GuardedBy("mPendingNetStackRequests") + private INetworkStackConnector mConnector; + + private volatile boolean mNetworkStackStartRequested = false; + + private interface NetworkStackCallback { + void onNetworkStackConnected(INetworkStackConnector connector); + } + + private NetworkStackClient() { } + + /** + * Get the NetworkStackClient singleton instance. + */ + public static synchronized NetworkStackClient getInstance() { + if (sInstance == null) { + sInstance = new NetworkStackClient(); + } + return sInstance; + } + + /** + * Create a DHCP server according to the specified parameters. + * + * <p>The server will be returned asynchronously through the provided callbacks. + */ + public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params, + final IDhcpServerCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeDhcpServer(ifName, params, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + /** + * Create an IpClient on the specified interface. + * + * <p>The IpClient will be returned asynchronously through the provided callbacks. + */ + public void makeIpClient(String ifName, IIpClientCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeIpClient(ifName, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + /** + * Create a NetworkMonitor. + * + * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks. + */ + public void makeNetworkMonitor( + NetworkParcelable network, String name, INetworkMonitorCallbacks cb) { + requestConnector(connector -> { + try { + connector.makeNetworkMonitor(network, name, cb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + }); + } + + private class NetworkStackConnection implements ServiceConnection { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + registerNetworkStackService(service); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // TODO: crash/reboot the system ? + Slog.wtf(TAG, "Lost network stack connector"); + } + }; + + private void registerNetworkStackService(@NonNull IBinder service) { + final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service); + + ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */, + DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + + final ArrayList<NetworkStackCallback> requests; + synchronized (mPendingNetStackRequests) { + requests = new ArrayList<>(mPendingNetStackRequests); + mPendingNetStackRequests.clear(); + mConnector = connector; + } + + for (NetworkStackCallback r : requests) { + r.onNetworkStackConnected(connector); + } + } + + /** + * Start the network stack. Should be called only once on device startup. + * + * <p>This method will start the network stack either in the network stack process, or inside + * the system server on devices that do not support the network stack module. The network stack + * connector will then be delivered asynchronously to clients that requested it before it was + * started. + */ + public void start(Context context) { + mNetworkStackStartRequested = true; + // Try to bind in-process if the library is available + IBinder connector = null; + try { + final Class service = Class.forName( + "com.android.server.NetworkStackService", + true /* initialize */, + context.getClassLoader()); + connector = (IBinder) service.getMethod("makeConnector", Context.class) + .invoke(null, context); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService"); + // TODO: crash/reboot system here ? + return; + } catch (ClassNotFoundException e) { + // Normal behavior if stack is provided by the app: fall through + } + + // In-process network stack. Add the service to the service manager here. + if (connector != null) { + registerNetworkStackService(connector); + return; + } + // Start the network stack process. The service will be added to the service manager in + // NetworkStackConnection.onServiceConnected(). + final Intent intent = new Intent(INetworkStackConnector.class.getName()); + final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); + intent.setComponent(comp); + + if (comp == null) { + Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + // TODO: crash/reboot system server ? + return; + } + final PackageManager pm = context.getPackageManager(); + int uid = -1; + try { + uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + } catch (PackageManager.NameNotFoundException e) { + Slog.wtf("Network stack package not found", e); + // Fall through + } + if (uid != Process.NETWORK_STACK_UID) { + throw new SecurityException("Invalid network stack UID: " + uid); + } + + final int hasPermission = + pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName()); + if (hasPermission != PERMISSION_GRANTED) { + throw new SecurityException( + "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK); + } + + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), + Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { + Slog.wtf(TAG, + "Could not bind to network stack in-process, or in app with " + intent); + // TODO: crash/reboot system server if no network stack after a timeout ? + } + } + + /** + * For non-system server clients, get the connector registered by the system server. + */ + private INetworkStackConnector getRemoteConnector() { + // Block until the NetworkStack connector is registered in ServiceManager. + // <p>This is only useful for non-system processes that do not have a way to be notified of + // registration completion. Adding a callback system would be too heavy weight considering + // that the connector is registered on boot, so it is unlikely that a client would request + // it before it is registered. + // TODO: consider blocking boot on registration and simplify much of the logic in this class + IBinder connector; + try { + final long before = System.currentTimeMillis(); + while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) { + Thread.sleep(20); + if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { + Slog.e(TAG, "Timeout waiting for NetworkStack connector"); + return null; + } + } + } catch (InterruptedException e) { + Slog.e(TAG, "Error waiting for NetworkStack connector", e); + return null; + } + + return INetworkStackConnector.Stub.asInterface(connector); + } + + private void requestConnector(@NonNull NetworkStackCallback request) { + // TODO: PID check. + final int caller = Binder.getCallingUid(); + if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) { + // Don't even attempt to obtain the connector and give a nice error message + throw new SecurityException( + "Only the system server should try to bind to the network stack."); + } + + if (!mNetworkStackStartRequested) { + // The network stack is not being started in this process, e.g. this process is not + // the system server. Get a remote connector registered by the system server. + final INetworkStackConnector connector = getRemoteConnector(); + synchronized (mPendingNetStackRequests) { + mConnector = connector; + } + request.onNetworkStackConnected(connector); + return; + } + + final INetworkStackConnector connector; + synchronized (mPendingNetStackRequests) { + connector = mConnector; + if (connector == null) { + mPendingNetStackRequests.add(request); + return; + } + } + + request.onNetworkStackConnected(connector); + } +} diff --git a/services/net/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java new file mode 100644 index 000000000000..bb56876c77f5 --- /dev/null +++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.dhcp; + +/** + * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion(). + * @hide + */ +public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub { + // TODO: add @Override here once the API is versioned + + /** + * Get the version of the aidl interface implemented by the callbacks. + */ + public int getInterfaceVersion() { + // TODO: return IDhcpServerCallbacks.VERSION; + return 0; + } +} diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java new file mode 100644 index 000000000000..db01ae4d4d9c --- /dev/null +++ b/services/net/java/android/net/ip/IpClientCallbacks.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ip; + +import android.net.DhcpResults; +import android.net.LinkProperties; + +/** + * Callbacks for handling IpClient events. + * + * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks, + * and avoid unparceling arguments. + * These methods are called asynchronously on a Binder thread, as IpClient lives in a different + * process. + * @hide + */ +public class IpClientCallbacks { + + /** + * Callback called upon IpClient creation. + * + * @param ipClient The Binder token to communicate with IpClient. + */ + public void onIpClientCreated(IIpClient ipClient) {} + + /** + * Callback called prior to DHCP discovery/renewal. + * + * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a + * ProvisioningConfiguration. + * + * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to + * indicate that DHCP is clear to proceed. + */ + public void onPreDhcpAction() {} + + /** + * Callback called after DHCP discovery/renewal. + */ + public void onPostDhcpAction() {} + + /** + * Callback called when new DHCP results are available. + * + * <p>This is purely advisory and not an indication of provisioning success or failure. This is + * only here for callers that want to expose DHCPv4 results to other APIs + * (e.g., WifiInfo#setInetAddress). + * + * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not + * the passed-in DhcpResults object is null. + */ + public void onNewDhcpResults(DhcpResults dhcpResults) {} + + /** + * Indicates that provisioning was successful. + */ + public void onProvisioningSuccess(LinkProperties newLp) {} + + /** + * Indicates that provisioning failed. + */ + public void onProvisioningFailure(LinkProperties newLp) {} + + /** + * Invoked on LinkProperties changes. + */ + public void onLinkPropertiesChange(LinkProperties newLp) {} + + /**Called when the internal IpReachabilityMonitor (if enabled) has + * detected the loss of a critical number of required neighbors. + */ + public void onReachabilityLost(String logMsg) {} + + /** + * Called when the IpClient state machine terminates. + */ + public void onQuit() {} + + /** + * Called to indicate that a new APF program must be installed to filter incoming packets. + */ + public void installPacketFilter(byte[] filter) {} + + /** + * Called to indicate that the APF Program & data buffer must be read asynchronously from the + * wifi driver. + * + * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire + * buffer. In response to this request, the driver returns the data buffer asynchronously + * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. + */ + public void startReadPacketFilter() {} + + /** + * If multicast filtering cannot be accomplished with APF, this function will be called to + * actuate multicast filtering using another means. + */ + public void setFallbackMulticastFilter(boolean enabled) {} + + /** + * Enabled/disable Neighbor Discover offload functionality. This is called, for example, + * whenever 464xlat is being started or stopped. + */ + public void setNeighborDiscoveryOffload(boolean enable) {} +} diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java index 2a2a67a92a86..bf917bf88b2d 100644 --- a/services/net/java/android/net/ip/IpClientUtil.java +++ b/services/net/java/android/net/ip/IpClientUtil.java @@ -23,8 +23,7 @@ import android.content.Context; import android.net.DhcpResultsParcelable; import android.net.LinkProperties; import android.net.LinkPropertiesParcelable; -import android.net.NetworkStack; -import android.net.ip.IIpClientCallbacks; +import android.net.NetworkStackClient; import android.os.ConditionVariable; import java.io.FileDescriptor; @@ -76,30 +75,17 @@ public class IpClientUtil { * * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of * {@link IIpClientCallbacks}. - * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)} + * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)} */ public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) { - context.getSystemService(NetworkStack.class) - .makeIpClient(ifName, new IpClientCallbacksProxy(callback)); - } - - /** - * Create a new IpClient. - * - * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy} - * instead of {@link IIpClientCallbacks}. - * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)} - */ - public static void makeIpClient( - Context context, String ifName, IpClientCallbacksProxy callback) { - context.getSystemService(NetworkStack.class) - .makeIpClient(ifName, callback); + // TODO: migrate clients and remove context argument + NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback)); } /** * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}. */ - public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { + private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { protected final IpClientCallbacks mCb; /** diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java index 7910c9a69310..34fc7354d63e 100644 --- a/services/net/java/android/net/ip/IpServer.java +++ b/services/net/java/android/net/ip/IpServer.java @@ -22,7 +22,6 @@ import static android.net.util.NetworkConstants.FF; import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH; import static android.net.util.NetworkConstants.asByte; -import android.content.Context; import android.net.ConnectivityManager; import android.net.INetd; import android.net.INetworkStackStatusCallback; @@ -31,7 +30,7 @@ import android.net.InterfaceConfiguration; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; -import android.net.NetworkStack; +import android.net.NetworkStackClient; import android.net.RouteInfo; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; @@ -132,10 +131,6 @@ public class IpServer extends StateMachine { } public static class Dependencies { - private final Context mContext; - public Dependencies(Context context) { - mContext = context; - } public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { return new RouterAdvertisementDaemon(ifParams); } @@ -153,7 +148,7 @@ public class IpServer extends StateMachine { */ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params, DhcpServerCallbacks cb) { - mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb); + NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb); } } diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java index 8df08262c9fa..8df08262c9fa 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java index 2af6f2bee8ff..2af6f2bee8ff 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java index 73baf80a2c70..73baf80a2c70 100644 --- a/services/robotests/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java +++ b/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml deleted file mode 100644 index 0f4e8a3ca0c6..000000000000 --- a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-two-refresh-intervals.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<certificates> - <metadata> - <serial> - 1000 - </serial> - <creation-time> - 1515697631 - </creation-time> - <refresh-interval> - 2592000 - </refresh-interval> - <refresh-interval> - 2592000 - </refresh-interval> - <previous> - <serial> - 0 - </serial> - <hash> - 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU= - </hash> - </previous> - </metadata> - <endpoints> - <cert> - MIIDCDCB8aADAgECAgYBYOlweDswDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi - R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAxMTEwODE1 - NTBaFw0yMDAxMTIwODE1NTBaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW - YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu - tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl - 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBABZALhC9j3hpZ0AgN0tsqAP2Ix21 - tNOcvo/aFJuSFanOM4DZbycZEYAo5rorvuFu7eXETBKDGnI5xreNAoQsaj/dyCHu - HKIn5P7yCmKvG2sV2TQ5go+0xV2x8BhTrtUWLeHvUbM3fXipa3NrordbA8MgzXwr - GR1Y1FuMOn5n4kiuHJ2sQTbDdzSQSK5VpH+6rjARlfOCyLUX0u8UKRRH81qhIQWb - UFMp9q1CVfiLP2O3CdDdpZXCysdflIb62TWnma+I8jqMryyxrMVs9kpfa8zkX9qe - 33Vxp+QaQTqQ07/7KYVw869MeFn+bXeHnjUhqGY6S8M71vrTMG3M5p8Sq9LmV8Y5 - 7YB5uqKap2Inf0FOuJS7h7nVVzU/kOFkepaQVHyScwTPuuXNgpQg8XZnN/AWfRwJ - hf5zE6vXXTHMzQA1mY2eEhxGfpryv7LH8pvfcyTakdBlw8aMJjKdre8xLLGZeVCa - 79plkfYD0rMrxtRHCGyTKGzUcx/B9kYJK5qBgJiDJLKF3XwGbAs/F8CyEPihjvj4 - M2EoeyhmHWKLYsps6+uTksJ+PxZU14M7672K2y8BdulyfkZIhili118XnRykKkMf - JLQJKMqZx5O0B9bF8yQdcGKEGEwMQt5ENdH8HeiwLm4QS3VzFXYetgUPCM5lPDIp - BuwwuQxvQDF4pmQd - </cert> - </endpoints> -</certificates> diff --git a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml index 3da012257d8f..3da012257d8f 100644 --- a/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/invalid-cert-file-no-refresh-interval.xml +++ b/services/tests/servicestests/assets/KeyStoreRecoveryControllerTest/xml/valid-cert-file-no-refresh-interval.xml diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java index 4bac200a22c6..ebbebcb02923 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java @@ -16,12 +16,12 @@ package com.android.server; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE; -import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; +import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_STANDBY; +import static android.net.INetd.FIREWALL_RULE_ALLOW; +import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.util.DebugUtils.valueToString; import static org.junit.Assert.assertEquals; diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index bd03a8d22643..04abeca1192e 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -403,7 +403,8 @@ public class UserControllerTest { protected int broadcastIntent(Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, - boolean sticky, int callingPid, int callingUid, int userId) { + boolean sticky, int callingPid, int callingUid, int realCallingUid, + int realCallingPid, int userId) { Log.i(TAG, "broadcastIntentLocked " + intent); mSentIntents.add(intent); return 0; diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java index a3f36b720398..3e5ce46e8e3a 100644 --- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java +++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java @@ -57,6 +57,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.backup.utils.RandomAccessFileUtils; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -125,6 +127,7 @@ public class TrampolineTest { private File mTestDir; private File mSuppressFile; private File mActivatedFile; + private File mRememberActivatedFile; @Before public void setUp() throws Exception { @@ -153,6 +156,8 @@ public class TrampolineTest { mActivatedFile = new File(mTestDir, "activate-" + NON_USER_SYSTEM); TrampolineTestable.sActivatedFiles.append(NON_USER_SYSTEM, mActivatedFile); + mRememberActivatedFile = new File(mTestDir, "rem-activate-" + NON_USER_SYSTEM); + TrampolineTestable.sRememberActivatedFiles.append(NON_USER_SYSTEM, mRememberActivatedFile); mTrampoline = new TrampolineTestable(mContextMock); } @@ -411,6 +416,34 @@ public class TrampolineTest { } @Test + public void setBackupServiceActive_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + + assertTrue(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, false)); + } + + @Test + public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test + public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() { + mTrampoline.initializeService(); + + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true); + mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false); + + assertFalse(RandomAccessFileUtils.readBoolean(mRememberActivatedFile, true)); + } + + @Test public void dataChanged_calledBeforeInitialize_ignored() throws Exception { mTrampoline.dataChanged(PACKAGE_NAME); verifyNoMoreInteractions(mBackupManagerServiceMock); @@ -1291,6 +1324,7 @@ public class TrampolineTest { static BackupManagerService sBackupManagerServiceMock = null; static File sSuppressFile = null; static SparseArray<File> sActivatedFiles = new SparseArray<>(); + static SparseArray<File> sRememberActivatedFiles = new SparseArray<>(); static UserManager sUserManagerMock = null; private int mCreateServiceCallsCount = 0; @@ -1314,6 +1348,11 @@ public class TrampolineTest { } @Override + protected File getRememberActivatedFileForNonSystemUser(int userId) { + return sRememberActivatedFiles.get(userId); + } + + @Override protected File getActivatedFileForNonSystemUser(int userId) { return sActivatedFiles.get(userId); } diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java index 0355e84d884d..5cb6cbb93b5b 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java @@ -556,16 +556,6 @@ public class IPackageManagerStub implements IPackageManager { } @Override - public byte[] getPermissionGrantBackup(int userId) throws RemoteException { - return new byte[0]; - } - - @Override - public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException { - - } - - @Override public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates) throws RemoteException { return null; diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java new file mode 100644 index 000000000000..eaa9c4520979 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/FileUtilsTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.google.common.io.Files; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class FileUtilsTest { + private static File sTemporaryDir; + private File mTemporaryFile; + + @BeforeClass + public static void setUpClass() { + sTemporaryDir = Files.createTempDir(); + } + + @AfterClass + public static void tearDownClass() { + if (sTemporaryDir != null) { + sTemporaryDir.delete(); + } + } + + @Before + public void setUp() throws Exception { + mTemporaryFile = new File(sTemporaryDir, "fileutilstest.txt"); + } + + /** Test that if file does not exist, {@link FileUtils#createNewFile()} creates the file. */ + @Test + public void testEnsureFileExists_fileDoesNotAlreadyExist_getsCreated() { + assertThat(!mTemporaryFile.exists()); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } + + /** Test that if file does exist, {@link FileUtils#createNewFile()} does not error out. */ + @Test + public void testEnsureFileExists_fileAlreadyExists_doesNotErrorOut() throws IOException { + mTemporaryFile.createNewFile(); + + FileUtils.createNewFile(mTemporaryFile); + + assertThat(mTemporaryFile.exists()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java new file mode 100644 index 000000000000..ca699bd7b85c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/backup/utils/RandomAccessFileUtilsTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup.utils; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class RandomAccessFileUtilsTest { + private File mTemporaryFile; + + @Before + public void setUp() throws Exception { + mTemporaryFile = File.createTempFile("fileutilstest", ".txt"); + } + + @After + public void tearDown() throws Exception { + if (mTemporaryFile != null) { + mTemporaryFile.delete(); + } + } + + /** + * Test that if we write true, we read back true. + */ + @Test + public void testWriteTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false, we read back false. + */ + @Test + public void testWriteFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true twice, we read back true. + */ + @Test + public void testWriteTrueTwice_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } + + /** + * Test that if we write false twice, we read back false. + */ + @Test + public void testWriteFalseTwice_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write true and then false, we read back false. + */ + @Test + public void testWriteTrueFalse_readReturnsFalse() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, true)).isEqualTo(false); + } + + /** + * Test that if we write false and then true, we read back true. + */ + @Test + public void testWriteFalseTrue_readReturnsTrue() { + RandomAccessFileUtils.writeBoolean(mTemporaryFile, false); + RandomAccessFileUtils.writeBoolean(mTemporaryFile, true); + + assertThat(RandomAccessFileUtils.readBoolean(mTemporaryFile, false)).isEqualTo(true); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java index 5900fc57296c..01759d2e8f4a 100644 --- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java @@ -98,6 +98,8 @@ public class ColorDisplayServiceTest { mColorDisplayService = new ColorDisplayService(mContext); mBinderService = mColorDisplayService.new BinderService(); + LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class, + mColorDisplayService.new ColorDisplayServiceInternal()); } @After @@ -110,6 +112,8 @@ public class ColorDisplayServiceTest { mUserId = UserHandle.USER_NULL; mContext = null; + + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); } @AfterClass @@ -979,6 +983,99 @@ public class ColorDisplayServiceTest { assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); } + @Test + public void displayWhiteBalance_enable() { + setWhiteBalance(true /* Enable DWB Setting */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + startService(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_disableAfterNightDisplayEnable() { + setWhiteBalance(true /* Enable DWB Setting */); + + startService(); + /* Enable nightlight */ + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + /* Since we are using FakeSettingsProvider which could not trigger observer change, + * force an update here.*/ + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + } + + @Test + public void displayWhiteBalance_enableAfterNightDisplayDisable() { + setWhiteBalance(true /* Enable DWB Setting */); + startService(); + /* Enable nightlight */ + setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); + setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); + + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(false); + + /* Disable nightlight */ + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_enableAfterLinearColorMode() { + setWhiteBalance(true /* Enable DWB Setting */); + setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); + startService(); + mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + + mColorDisplayService.updateDisplayWhiteBalanceStatus(); + assertDwbActive(true); + } + + @Test + public void displayWhiteBalance_setTemperatureOverMax() { + int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(max); + } + + @Test + public void displayWhiteBalance_setTemperatureBelowMin() { + int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(min); + } + + @Test + public void displayWhiteBalance_setValidTemperature() { + int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; + int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + int valToSet = (min + max) / 2; + + ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet); + + assertWithMessage("Unexpected temperature set") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .isEqualTo(valToSet); + } + /** * Configures Night display to use a custom schedule. * @@ -1041,6 +1138,16 @@ public class ColorDisplayServiceTest { } /** + * Configures the Display White Balance setting state. + * + * @param state {@code true} if display white balance should be enabled + */ + private void setWhiteBalance(boolean state) { + Secure.putIntForUser(mContext.getContentResolver(), + Secure.DISPLAY_WHITE_BALANCE_ENABLED, state ? 1 : 0, mUserId); + } + + /** * Configures color mode. */ private void setColorMode(int colorMode) { @@ -1111,6 +1218,17 @@ public class ColorDisplayServiceTest { } /** + * Convenience method for asserting that the DWB active status matches expectation. + * + * @param enabled the expected active status. + */ + private void assertDwbActive(boolean enabled) { + assertWithMessage("Incorrect Display White Balance state") + .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated()) + .isEqualTo(enabled); + } + + /** * Convenience for making a {@link LocalTime} instance with an offset relative to now. * * @param offsetMinutes the offset relative to now (in minutes) diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java index bbcc41113f5a..9836c64ea5b5 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/certificate/CertXmlTest.java @@ -47,7 +47,6 @@ public final class CertXmlTest { public void parse_succeeds() throws Exception { CertXml certXml = CertXml.parse(certXmlBytes); assertThat(certXml.getSerial()).isEqualTo(1000L); - assertThat(certXml.getRefreshInterval()).isEqualTo(2592000L); } @Test @@ -75,27 +74,22 @@ public final class CertXmlTest { } @Test - public void parse_throwsIfNoEndpointCert() throws Exception { - CertParsingException expected = - expectThrows( - CertParsingException.class, - () -> - CertXml.parse( - TestData.readTestFile( - "xml/invalid-cert-file-no-endpoint-cert.xml"))); - assertThat(expected.getMessage()).contains("at least one"); + public void parse_doesNotThrowIfNoRefreshInterval() throws Exception { + CertXml.parse( + TestData.readTestFile( + "xml/valid-cert-file-no-refresh-interval.xml")); } @Test - public void parse_throwsIfNoRefreshInterval() throws Exception { + public void parse_throwsIfNoEndpointCert() throws Exception { CertParsingException expected = expectThrows( CertParsingException.class, () -> CertXml.parse( TestData.readTestFile( - "xml/invalid-cert-file-no-refresh-interval.xml"))); - assertThat(expected.getMessage()).contains("exactly one"); + "xml/invalid-cert-file-no-endpoint-cert.xml"))); + assertThat(expected.getMessage()).contains("at least one"); } @Test @@ -111,19 +105,6 @@ public final class CertXmlTest { } @Test - public void parse_throwsIfTwoRefreshIntervals() throws Exception { - CertParsingException expected = - expectThrows( - CertParsingException.class, - () -> - CertXml.parse( - TestData.readTestFile( - "xml/invalid-cert-file-two-refresh-intervals" - + ".xml"))); - assertThat(expected.getMessage()).contains("exactly one"); - } - - @Test public void parse_throwsIfTwoSerials() throws Exception { CertParsingException expected = expectThrows( diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java index 6d28ed19af4f..50734efacac9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java @@ -55,8 +55,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.AtomicFile; import com.android.server.LocalServices; -import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import org.junit.After; import org.junit.Before; @@ -88,7 +88,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -103,7 +104,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -120,7 +122,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -143,7 +146,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -313,7 +317,8 @@ public class PackageManagerSettingsTests { writeOldFiles(); final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); Settings settings = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); assertThat(settings.readLPw(createFakeUsers()), is(true)); @@ -507,7 +512,8 @@ public class PackageManagerSettingsTests { public void testUpdatePackageSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); final Settings testSettings01 = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( @@ -625,7 +631,8 @@ public class PackageManagerSettingsTests { public void testCreateNewSetting03() { final Context context = InstrumentationRegistry.getContext(); final Object lock = new Object(); - PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock); + PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null, + lock); final Settings testSettings01 = new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock); final SharedUserSetting testUserSetting01 = createSharedUserSetting( diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index 48ab8d6698bd..0196279cbf56 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -568,7 +568,8 @@ public class DexManagerTests { } private PackageDynamicCode getPackageDynamicCodeInfo(TestData testData) { - return mDexManager.getDexLogger().getPackageDynamicCodeInfo(testData.getPackageName()); + return mDexManager.getDynamicCodeLogger() + .getPackageDynamicCodeInfo(testData.getPackageName()); } private void assertNoUseInfo(TestData testData) { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java index 6da202b93065..7992ba33c8da 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DynamicCodeLoggerTests.java @@ -53,7 +53,7 @@ import org.mockito.stubbing.Stubber; @RunWith(AndroidJUnit4.class) @SmallTest -public class DexLoggerTests { +public class DynamicCodeLoggerTests { private static final String OWNING_PACKAGE_NAME = "package.name"; private static final String VOLUME_UUID = "volUuid"; private static final String FILE_PATH = "/bar/foo.jar"; @@ -85,7 +85,7 @@ public class DexLoggerTests { @Mock IPackageManager mPM; @Mock Installer mInstaller; - private DexLogger mDexLogger; + private DynamicCodeLogger mDynamicCodeLogger; private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create(); private boolean mWriteTriggered = false; @@ -106,7 +106,7 @@ public class DexLoggerTests { }; // For test purposes capture log messages as well as sending to the event log. - mDexLogger = new DexLogger(mPM, mInstaller, packageDynamicCodeLoading) { + mDynamicCodeLogger = new DynamicCodeLogger(mPM, mInstaller, packageDynamicCodeLoading) { @Override void writeDclEvent(String subtag, int uid, String message) { super.writeDclEvent(subtag, uid, message); @@ -131,13 +131,13 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES)); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isFalse(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); } @@ -146,14 +146,14 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(EMPTY_BYTES)); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH); // File should be removed from the DCL list, since we can't hash it. assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test @@ -162,24 +162,24 @@ public class DexLoggerTests { doThrow(new InstallerException("Intentional failure for test"))); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITHOUT_CONTENT_HASH); // File should be removed from the DCL list, since we can't hash it. assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test public void testOneLoader_ownFile_unknownPath() { recordLoad(OWNING_PACKAGE_NAME, "other/path"); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } @Test @@ -189,7 +189,7 @@ public class DexLoggerTests { setPackageUid(OWNING_PACKAGE_NAME, -1); recordLoad(OWNING_PACKAGE_NAME, filePath); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); } @@ -200,7 +200,7 @@ public class DexLoggerTests { setPackageUid("other.package.name", 1001); recordLoad("other.package.name", FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(1001); assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH); @@ -213,7 +213,7 @@ public class DexLoggerTests { setPackageUid("other.package.name", -1); recordLoad("other.package.name", FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isFalse(); @@ -224,14 +224,14 @@ public class DexLoggerTests { whenFileIsHashed(FILE_PATH, doReturn(CONTENT_HASH_BYTES)); recordLoadNative(FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID); assertThat(mMessagesForUid) .containsEntry(OWNER_UID, EXPECTED_MESSAGE_NATIVE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isFalse(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); } @@ -247,7 +247,7 @@ public class DexLoggerTests { recordLoad("other.package.name1", otherDexPath); recordLoad("other.package.name2", FILE_PATH); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID); assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH); @@ -256,10 +256,10 @@ public class DexLoggerTests { assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()) + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()) .containsExactly(OWNING_PACKAGE_NAME); - // Check the DexLogger caching is working + // Check the DynamicCodeLogger caching is working verify(mPM, atMost(1)).getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID); } @@ -267,7 +267,7 @@ public class DexLoggerTests { public void testUnknownOwner() { reset(mPM); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading("other.package.name"); + mDynamicCodeLogger.logDynamicCodeLoading("other.package.name"); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isFalse(); @@ -278,11 +278,11 @@ public class DexLoggerTests { public void testUninstalledPackage() { reset(mPM); recordLoad(OWNING_PACKAGE_NAME, FILE_PATH); - mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); + mDynamicCodeLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME); assertThat(mMessagesForUid).isEmpty(); assertThat(mWriteTriggered).isTrue(); - assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); + assertThat(mDynamicCodeLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty(); } private void setPackageUid(String packageName, int uid) throws Exception { @@ -295,7 +295,8 @@ public class DexLoggerTests { } private void recordLoad(String loadingPackageName, String dexPath) { - mDexLogger.recordDex(OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName); + mDynamicCodeLogger.recordDex( + OWNER_USER_ID, dexPath, OWNING_PACKAGE_NAME, loadingPackageName); mWriteTriggered = false; } @@ -304,7 +305,7 @@ public class DexLoggerTests { String[] packageNames = { OWNING_PACKAGE_NAME }; when(mPM.getPackagesForUid(loadingUid)).thenReturn(packageNames); - mDexLogger.recordNative(loadingUid, nativePath); + mDynamicCodeLogger.recordNative(loadingUid, nativePath); mWriteTriggered = false; } } diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java index 9f1cbcd7ec27..6a937fabd3ec 100644 --- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java @@ -98,6 +98,15 @@ public class AttentionDetectorTest extends AndroidTestCase { } @Test + public void testUpdateUserActivity_schedulesTheNextCheck() { + long now = SystemClock.uptimeMillis(); + mNextDimming = now; + mAttentionDetector.onUserActivity(now, PowerManager.USER_ACTIVITY_EVENT_TOUCH); + long nextTimeout = mAttentionDetector.updateUserActivity(mNextDimming + 5000L); + assertThat(nextTimeout).isEqualTo(mNextDimming + 5000L); + } + + @Test public void testOnUserActivity_ignoresAfterMaximumExtension() { long now = SystemClock.uptimeMillis(); mAttentionDetector.onUserActivity(now - 15000L, PowerManager.USER_ACTIVITY_EVENT_TOUCH); diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 63341b6ea38a..911c4a2f4122 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -21,15 +21,21 @@ import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; +import android.attention.AttentionManagerInternal; import android.content.Context; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.BatteryManagerInternal; +import android.os.Binder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; @@ -53,6 +59,9 @@ import org.junit.Rule; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.HashMap; +import java.util.Map; + /** * Tests for {@link com.android.server.power.PowerManagerService} */ @@ -67,6 +76,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { private @Mock DisplayManagerInternal mDisplayManagerInternalMock; private @Mock BatteryManagerInternal mBatteryManagerInternalMock; private @Mock ActivityManagerInternal mActivityManagerInternalMock; + private @Mock AttentionManagerInternal mAttentionManagerInternalMock; private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock; private @Mock Notifier mNotifierMock; private PowerManagerService mService; @@ -93,6 +103,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock); addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock); addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); mService = new PowerManagerService(getContext(), new Injector() { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, @@ -210,4 +221,80 @@ public class PowerManagerServiceTest extends AndroidTestCase { mService.onUserActivity(); assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse(); } + + @SmallTest + public void testForceSuspend_putsDeviceToSleep() { + mService.systemReady(null); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Verify that we start awake + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + + // Grab the wakefulness value when PowerManager finally calls into the + // native component to actually perform the suspend. + when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> { + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + return true; + }); + + boolean retval = mService.getBinderServiceInstance().forceSuspend(); + assertThat(retval).isTrue(); + + // Still asleep when the function returns. + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + } + + @SmallTest + public void testForceSuspend_pakeLocksDisabled() { + final String tag = "TestWakelockTag_098213"; + final int flags = PowerManager.PARTIAL_WAKE_LOCK; + final String pkg = getContext().getOpPackageName(); + + // Set up the Notification mock to keep track of the wakelocks that are currently + // active or disabled. We'll use this to verify that wakelocks are disabled when + // they should be. + final Map<String, Integer> wakelockMap = new HashMap<>(1); + doAnswer(inv -> { + wakelockMap.put((String) inv.getArguments()[1], (int) inv.getArguments()[0]); + return null; + }).when(mNotifierMock).onWakeLockAcquired(anyInt(), anyString(), anyString(), anyInt(), + anyInt(), any(), any()); + doAnswer(inv -> { + wakelockMap.remove((String) inv.getArguments()[1]); + return null; + }).when(mNotifierMock).onWakeLockReleased(anyInt(), anyString(), anyString(), anyInt(), + anyInt(), any(), any()); + + // + // TEST STARTS HERE + // + mService.systemReady(null); + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + + // Verify that we start awake + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + + // Create a wakelock + mService.getBinderServiceInstance().acquireWakeLock(new Binder(), flags, tag, pkg, + null /* workSource */, null /* historyTag */); + assertThat(wakelockMap.get(tag)).isEqualTo(flags); // Verify wakelock is active. + + // Confirm that the wakelocks have been disabled when the forceSuspend is in flight. + when(mNativeWrapperMock.nativeForceSuspend()).then(inv -> { + // Verify that the wakelock is disabled by the time we get to the native force + // suspend call. + assertThat(wakelockMap.containsKey(tag)).isFalse(); + return true; + }); + + assertThat(mService.getBinderServiceInstance().forceSuspend()).isTrue(); + assertThat(wakelockMap.get(tag)).isEqualTo(flags); + + } + + @SmallTest + public void testForceSuspend_forceSuspendFailurePropogated() { + when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); + assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 8c36905d8422..a1db3e8d8ded 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -27,9 +27,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_LEFT; -import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; @@ -158,35 +155,6 @@ public class ActivityRecordTests extends ActivityTestsBase { assertTrue(mActivity.isState(STOPPED)); } - @Test - public void testPositionLimitedAspectRatioNavBarBottom() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f, - new Rect(0, 0, 1000, 1500)); - } - - @Test - public void testPositionLimitedAspectRatioNavBarLeft() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f, - new Rect(500, 0, 2000, 1000)); - } - - @Test - public void testPositionLimitedAspectRatioNavBarRight() { - verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f, - new Rect(0, 0, 1500, 1000)); - } - - private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds, - float aspectRatio, Rect expectedActivityBounds) { - // Verify with nav bar on the right. - when(mService.mWindowManager.getNavBarPosition(mActivity.getDisplayId())) - .thenReturn(navBarPosition); - mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds); - mActivity.info.maxAspectRatio = aspectRatio; - ensureActivityConfiguration(); - assertEquals(expectedActivityBounds, mActivity.getBounds()); - } - private void ensureActivityConfiguration() { mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index a7520dcbcff9..2627ec762d7a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertTrue; import android.graphics.Rect; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.view.Display; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -48,6 +49,7 @@ import org.junit.Test; * atest WmTests:AppChangeTransitionTests */ @SmallTest +@Presubmit public class AppChangeTransitionTests extends WindowTestsBase { private TaskStack mStack; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index cd1320986972..1dd72ec4fd71 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -51,7 +51,6 @@ import android.platform.test.annotations.Presubmit; import android.view.Surface; import android.view.WindowManager; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -142,7 +141,6 @@ public class AppWindowTokenTests extends WindowTestsBase { mToken.removeImmediately(); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testLandscapeSeascapeRotationByApp() { // Some plumbing to get the service ready for rotation updates. @@ -303,7 +301,6 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test - @FlakyTest(detail = "Promote once confirmed non-flaky") public void testStuckExitingWindow() { final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, "closingWindow"); @@ -346,7 +343,6 @@ public class AppWindowTokenTests extends WindowTestsBase { assertNoStartingWindow(mToken); } - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") @Test public void testAddRemoveRace() { // There was once a race condition between adding and removing starting windows diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java index 3f83caea613a..1e02a12e83db 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java @@ -46,6 +46,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.platform.test.annotations.Presubmit; import android.util.Log; import android.view.IWindowManager; @@ -71,6 +72,7 @@ import java.util.concurrent.TimeUnit; * atest WmTests:AssistDataRequesterTest */ @MediumTest +@Presubmit public class AssistDataRequesterTest extends ActivityTestsBase { private static final String TAG = AssistDataRequesterTest.class.getSimpleName(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index a62bc713db40..2452ef0425e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -61,7 +61,6 @@ import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.view.DisplayCutout; -import android.view.DisplayInfo; import android.view.Gravity; import android.view.MotionEvent; import android.view.Surface; @@ -87,18 +86,22 @@ import java.util.List; * Tests for the {@link DisplayContent} class. * * Build/Install/Run: - * atest FrameworksServicesTests:DisplayContentTests + * atest WmTests:DisplayContentTests */ @SmallTest @Presubmit public class DisplayContentTests extends WindowTestsBase { @Test - @FlakyTest(bugId = 77772044) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testForAllWindows() { final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "exiting app"); final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken; + // Wait until everything in animation handler get executed to prevent the exiting window + // from being removed during WindowSurfacePlacer Traversal. + waitUntilHandlersIdle(); + exitingAppToken.mIsExiting = true; exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index 198e7ce63f52..b15e99aaa8c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -60,6 +60,7 @@ import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.utils.WmDisplayCutout; import org.junit.After; import org.junit.Before; @@ -113,6 +114,7 @@ public class DisplayRotationTests { public static void setUpOnce() { sMockWm = mock(WindowManagerService.class); sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class); + sMockWm.mPolicy = mock(WindowManagerPolicy.class); } @Before @@ -807,6 +809,8 @@ public class DisplayRotationTests { mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; + when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) + .thenReturn(WmDisplayCutout.NO_CUTOUT); mMockDisplayPolicy = mock(DisplayPolicy.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index bc62e8c5ab24..2d906d1683d5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static org.junit.Assert.assertEquals; @@ -28,15 +29,33 @@ import static org.junit.Assert.assertNull; import android.platform.test.annotations.Presubmit; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ViewRootImpl; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; @SmallTest @Presubmit public class InsetsStateControllerTest extends WindowTestsBase { + private static int sPreviousNewInsetsMode; + + @BeforeClass + public static void setUpOnce() { + // TODO: Make use of SettingsSession when it becomes feasible for this. + sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode; + // To let the insets provider control the insets visibility, the insets mode has to be + // NEW_INSETS_MODE_FULL. + ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL; + } + + @AfterClass + public static void tearDownOnce() { + ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode; + } @Test public void testStripForDispatch_notOwn() { @@ -47,7 +66,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR)); } - @FlakyTest(bugId = 69229402) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_own() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -57,7 +76,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar)); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testStripForDispatch_navBar() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -69,7 +88,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar)); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testBarControllingWinChanged() { final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); @@ -82,7 +101,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertEquals(2, controls.length); } - @FlakyTest(bugId = 124088319) + @FlakyTest(detail = "Promote to pre-submit once confirmed stable.") @Test public void testControlRevoked() { final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow"); diff --git a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java index ce22788f7cb9..df26679dc1fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/KeyguardDisableHandlerTest.java @@ -34,6 +34,7 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; import android.util.SparseBooleanArray; import com.android.server.wm.LockTaskController.LockTaskToken; @@ -43,6 +44,7 @@ import org.junit.Test; import java.lang.reflect.Constructor; +@Presubmit public class KeyguardDisableHandlerTest { private KeyguardDisableHandler mKeyguardDisable; diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index cc6a58a81635..763ea6293fcc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -112,8 +112,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } } - @FlakyTest(bugId = 117117823) @Test + @FlakyTest(bugId = 117117823) public void testIncludedApps_expectTargetAndVisible() { mWm.setRecentsAnimationController(mController); final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java index c595868db484..2377df406fbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java @@ -34,10 +34,12 @@ import static org.junit.Assert.assertTrue; import android.app.IActivityTaskManager; import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Before; @@ -50,6 +52,8 @@ import org.junit.Test; * atest WmTests:TaskPositionerTests */ @SmallTest +@Presubmit +@FlakyTest public class TaskPositionerTests extends WindowTestsBase { private static final boolean DEBUGGING = false; diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index bcf9dd218835..388f98ffe4a3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -24,6 +24,7 @@ import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -307,6 +308,41 @@ public class TaskRecordTests extends ActivityTestsBase { assertEquals(fullScreenBounds, task.getBounds()); } + @Test + public void testComputeConfigResourceOverrides() { + final TaskRecord task = new TaskBuilder(mSupervisor).build(); + final Configuration inOutConfig = new Configuration(); + final Configuration parentConfig = new Configuration(); + final int longSide = 1200; + final int shortSide = 600; + parentConfig.densityDpi = 400; + parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px + parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px + + // Portrait bounds. + inOutConfig.windowConfiguration.getBounds().set(0, 0, shortSide, longSide); + // By default, the parent bounds should limit the existing input bounds. + task.computeConfigResourceOverrides(inOutConfig, parentConfig); + + assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); + assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); + assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); + + inOutConfig.setToDefaults(); + // Landscape bounds. + inOutConfig.windowConfiguration.getBounds().set(0, 0, longSide, shortSide); + // Without limiting to be inside the parent bounds, the out screen size should keep relative + // to the input bounds. + task.computeConfigResourceOverrides(inOutConfig, parentConfig, + false /* insideParentBounds */); + + assertEquals(shortSide * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenHeightDp); + assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, + inOutConfig.screenWidthDp); + assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); + } + /** Ensures that the alias intent won't have target component resolved. */ @Test public void testTaskIntentActivityAlias() { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java index dcca3167c752..42a205a2fb3d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java @@ -100,7 +100,7 @@ public class TaskStackChangedListenerTest { } @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testTaskDescriptionChanged() throws Exception { final Object[] params = new Object[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -122,14 +122,18 @@ public class TaskStackChangedListenerTest { } } }); - final Activity activity = startTestActivity(ActivityTaskDescriptionChange.class); + + int taskId; + synchronized (sLock) { + taskId = startTestActivity(ActivityTaskDescriptionChange.class).getTaskId(); + } waitForCallback(latch); - assertEquals(activity.getTaskId(), params[0]); + assertEquals(taskId, params[0]); assertEquals("Test Label", ((TaskDescription) params[1]).getLabel()); } @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testActivityRequestedOrientationChanged() throws Exception { final int[] params = new int[2]; final CountDownLatch latch = new CountDownLatch(1); @@ -142,9 +146,12 @@ public class TaskStackChangedListenerTest { latch.countDown(); } }); - final Activity activity = startTestActivity(ActivityRequestedOrientationChange.class); + int taskId; + synchronized (sLock) { + taskId = startTestActivity(ActivityRequestedOrientationChange.class).getTaskId(); + } waitForCallback(latch); - assertEquals(activity.getTaskId(), params[0]); + assertEquals(taskId, params[0]); assertEquals(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, params[1]); } @@ -152,7 +159,7 @@ public class TaskStackChangedListenerTest { * Tests for onTaskCreated, onTaskMovedToFront, onTaskRemoved and onTaskRemovalStarted. */ @Test - @FlakyTest(bugId = 119893767) + @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testTaskChangeCallBacks() throws Exception { final Object[] params = new Object[2]; final CountDownLatch taskCreatedLaunchLatch = new CountDownLatch(1); @@ -245,7 +252,7 @@ public class TaskStackChangedListenerTest { private void waitForCallback(CountDownLatch latch) { try { - final boolean result = latch.await(2, TimeUnit.SECONDS); + final boolean result = latch.await(4, TimeUnit.SECONDS); if (!result) { throw new RuntimeException("Timed out waiting for task stack change notification"); } @@ -324,7 +331,11 @@ public class TaskStackChangedListenerTest { protected void onPostResume() { super.onPostResume(); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); - finish(); + synchronized (sLock) { + // Hold the lock to ensure no one is trying to access fields of this Activity in + // this test. + finish(); + } } } @@ -333,7 +344,11 @@ public class TaskStackChangedListenerTest { protected void onPostResume() { super.onPostResume(); setTaskDescription(new TaskDescription("Test Label")); - finish(); + synchronized (sLock) { + // Hold the lock to ensure no one is trying to access fields of this Activity in + // this test. + finish(); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 3eb9085b68f6..b0e20b89b811 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -54,7 +55,6 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; import android.graphics.Insets; import android.graphics.Matrix; @@ -87,6 +87,7 @@ import java.util.LinkedList; */ @SmallTest @Presubmit +@FlakyTest(bugId = 124127512) public class WindowStateTests extends WindowTestsBase { private static int sPreviousNewInsetsMode; @@ -389,6 +390,7 @@ public class WindowStateTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 74078662) public void testLayoutSeqResetOnReparent() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); app.mLayoutSeq = 1; @@ -445,6 +447,7 @@ public class WindowStateTests extends WindowTestsBase { } @Test + @FlakyTest(bugId = 74078662) public void testDisplayCutoutIsCalculatedRelativeToFrame() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); WindowFrames wf = app.getWindowFrames(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java index 2b8e307e23b7..b299f0dd7253 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java @@ -36,11 +36,10 @@ import org.junit.Before; import org.junit.Test; import java.io.File; -import java.io.IOException; /** - * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}. + * Test class for {@link WindowTraceBuffer}. * * Build/Install/Run: * atest WmTests:WindowTraceBufferTest @@ -49,12 +48,15 @@ import java.io.IOException; @Presubmit public class WindowTraceBufferTest { private File mFile; + private WindowTraceBuffer mBuffer; @Before public void setUp() throws Exception { final Context testContext = getInstrumentation().getContext(); mFile = testContext.getFileStreamPath("tracing_test.dat"); mFile.delete(); + + mBuffer = new WindowTraceBuffer(10); } @After @@ -63,145 +65,112 @@ public class WindowTraceBufferTest { } @Test - public void testTraceQueueBuffer_addItem() throws Exception { - ProtoOutputStream toWrite1 = getDummy(1); - ProtoOutputStream toWrite2 = getDummy(2); - ProtoOutputStream toWrite3 = getDummy(3); - final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2; - - final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity); - - buffer.add(toWrite1); - byte[] toWrite1Bytes = toWrite1.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - - buffer.add(toWrite2); - byte[] toWrite2Bytes = toWrite2.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - - buffer.add(toWrite3); - byte[] toWrite3Bytes = toWrite3.getBytes(); - assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); - assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); - assertTrue("Third element should not be in the list", - !buffer.contains(toWrite3Bytes)); - - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); - assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals("Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); - } - - @Test - public void testTraceRingBuffer_addItem() throws Exception { + public void test_addItem() { ProtoOutputStream toWrite = getDummy(1); final int objectSize = toWrite.getRawSize(); + mBuffer.setCapacity(objectSize); + mBuffer.resetBuffer(); - final WindowTraceBuffer buffer = buildRingBuffer(objectSize); - - Preconditions.checkArgument(buffer.mBuffer.isEmpty()); + Preconditions.checkArgument(mBuffer.size() == 0); - buffer.add(toWrite); + mBuffer.add(toWrite); - assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); assertEquals("Total buffer getSize differs from inserted object", - buffer.mBufferSize, objectSize); - assertEquals("Available buffer space does not match used one", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), objectSize); + assertEquals("Available buffer space does not match used one", 0, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception { + public void test_addItemMustOverwriteOne() { ProtoOutputStream toWrite1 = getDummy(1); ProtoOutputStream toWrite2 = getDummy(2); ProtoOutputStream toWrite3 = getDummy(3); final int objectSize = toWrite1.getRawSize(); - final int bufferCapacity = objectSize * 2 + 1; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); - buffer.add(toWrite1); + mBuffer.add(toWrite1); byte[] toWrite1Bytes = toWrite1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); - buffer.add(toWrite2); + mBuffer.add(toWrite2); byte[] toWrite2Bytes = toWrite2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWrite1Bytes)); + mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); - buffer.add(toWrite3); + mBuffer.add(toWrite3); byte[] toWrite3Bytes = toWrite3.getBytes(); assertTrue("First element should not be in the list", - !buffer.contains(toWrite1Bytes)); + !mBuffer.contains(toWrite1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWrite2Bytes)); + mBuffer.contains(toWrite2Bytes)); assertTrue("Third element should be in the list", - buffer.contains(toWrite3Bytes)); - assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2); + mBuffer.contains(toWrite3Bytes)); + assertEquals("Buffer should have 2 elements", 2, mBuffer.size()); assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity - 1); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 1); + mBuffer.getBufferSize(), bufferCapacity - 1); + assertEquals(" Buffer is full, available space should be 0", 1, + mBuffer.getAvailableSpace()); } @Test - public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception { + public void test_addItemMustOverwriteMultiple() { ProtoOutputStream toWriteSmall1 = getDummy(1); ProtoOutputStream toWriteSmall2 = getDummy(2); final int objectSize = toWriteSmall1.getRawSize(); - final int bufferCapacity = objectSize * 2; - final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity); + mBuffer.setCapacity(bufferCapacity); + mBuffer.resetBuffer(); ProtoOutputStream toWriteBig = new ProtoOutputStream(); toWriteBig.write(MAGIC_NUMBER, 1); toWriteBig.write(MAGIC_NUMBER, 2); - buffer.add(toWriteSmall1); + mBuffer.add(toWriteSmall1); byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); - buffer.add(toWriteSmall2); + mBuffer.add(toWriteSmall2); byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes(); assertTrue("First element should be in the list", - buffer.contains(toWriteSmall1Bytes)); + mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Second element should be in the list", - buffer.contains(toWriteSmall2Bytes)); + mBuffer.contains(toWriteSmall2Bytes)); - buffer.add(toWriteBig); + mBuffer.add(toWriteBig); byte[] toWriteBigBytes = toWriteBig.getBytes(); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall1Bytes)); + !mBuffer.contains(toWriteSmall1Bytes)); assertTrue("Third element should overwrite all others", - !buffer.contains(toWriteSmall2Bytes)); + !mBuffer.contains(toWriteSmall2Bytes)); assertTrue("Third element should overwrite all others", - buffer.contains(toWriteBigBytes)); + mBuffer.contains(toWriteBigBytes)); - assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1); + assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size()); assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity), - buffer.mBufferSize, bufferCapacity); - assertEquals(" Buffer is full, available space should be 0", - buffer.getAvailableSpace(), 0); + mBuffer.getBufferSize(), bufferCapacity); + assertEquals(" Buffer is full, available space should be 0", 0, + mBuffer.getAvailableSpace()); } - private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException { - return new WindowTraceBuffer.Builder() - .setContinuousMode(true) - .setBufferCapacity(capacity) - .setTraceFile(mFile) - .build(); + @Test + public void test_startResetsBuffer() { + ProtoOutputStream toWrite = getDummy(1); + mBuffer.resetBuffer(); + Preconditions.checkArgument(mBuffer.size() == 0); + + mBuffer.add(toWrite); + assertEquals("Item was not added to the buffer", 1, mBuffer.size()); + mBuffer.resetBuffer(); + assertEquals("Buffer should be empty after reset", 0, mBuffer.size()); + assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize()); } private ProtoOutputStream getDummy(int value) { @@ -212,7 +181,4 @@ public class WindowTraceBufferTest { return toWrite; } - private WindowTraceBuffer buildQueueBuffer(int size) throws IOException { - return new WindowTraceQueueBuffer(size, mFile, false); - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java index 3c6e2405adff..8358fdd18e0e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java @@ -88,8 +88,7 @@ public class WindowTracingTest { mFile.delete(); mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer, - new WindowManagerGlobalLock()); - mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */); + new WindowManagerGlobalLock(), 1024); } @After @@ -103,13 +102,13 @@ public class WindowTracingTest { } @Test - public void isEnabled_returnsTrueAfterStart() throws Exception { + public void isEnabled_returnsTrueAfterStart() { mWindowTracing.startTrace(mock(PrintWriter.class)); assertTrue(mWindowTracing.isEnabled()); } @Test - public void isEnabled_returnsFalseAfterStop() throws Exception { + public void isEnabled_returnsFalseAfterStop() { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); assertFalse(mWindowTracing.isEnabled()); @@ -133,6 +132,8 @@ public class WindowTracingTest { mWindowTracing.startTrace(mock(PrintWriter.class)); mWindowTracing.stopTrace(mock(PrintWriter.class)); + assertTrue("Trace file should exist", mFile.exists()); + byte[] header = new byte[MAGIC_HEADER.length]; try (InputStream is = new FileInputStream(mFile)) { assertEquals(MAGIC_HEADER.length, is.read(header)); diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java index 649b785c992b..99ceb2011db3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/CoordinateTransformsTest.java @@ -31,6 +31,7 @@ import static org.junit.Assert.*; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; +import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import org.junit.Before; @@ -42,6 +43,7 @@ import org.junit.rules.ErrorCollector; * Build/Install/Run: * atest WmTests:CoordinateTransformsTest */ +@Presubmit public class CoordinateTransformsTest { private static final int W = 200; diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index f1ddfe4cd0d5..8feed7fdb785 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -15,7 +15,6 @@ */ package com.android.server.usage; -import static android.app.usage.UsageEvents.Event.ACTIVITY_DESTROYED; import static android.app.usage.UsageEvents.Event.ACTIVITY_PAUSED; import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; import static android.app.usage.UsageEvents.Event.ACTIVITY_STOPPED; @@ -302,27 +301,6 @@ public class IntervalStats { UsageStats usageStats = packageStats.valueAt(i); usageStats.update(null, timeStamp, eventType, instanceId); } - } else if (eventType == ACTIVITY_DESTROYED) { - UsageStats usageStats = packageStats.get(packageName); - if (usageStats != null) { - // If previous event is not ACTIVITY_STOPPED, convert ACTIVITY_DESTROYED - // to ACTIVITY_STOPPED and add to event list. - // Otherwise do not add anything to event list. (Because we want to save space - // and we do not want a ACTIVITY_STOPPED followed by - // ACTIVITY_DESTROYED in event list). - final int index = usageStats.mActivities.indexOfKey(instanceId); - if (index >= 0) { - final int type = usageStats.mActivities.valueAt(index); - if (type != ACTIVITY_STOPPED) { - Event event = new Event(ACTIVITY_STOPPED, timeStamp); - event.mPackage = packageName; - event.mClass = className; - event.mInstanceId = instanceId; - addEvent(event); - } - } - usageStats.update(className, timeStamp, ACTIVITY_DESTROYED, instanceId); - } } else { UsageStats usageStats = getOrCreateUsageStats(packageName); usageStats.update(className, timeStamp, eventType, instanceId); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index af5278f222b8..ebb0210cb553 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -145,8 +145,16 @@ public class UsageStatsService extends SystemService implements AppTimeLimitController mAppTimeLimit; final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); - final SparseArray<String> mVisibleActivities = new SparseArray(); + final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); + private static class ActivityData { + private final String mTaskRootPackage; + private final String mTaskRootClass; + private ActivityData(String taskRootPackage, String taskRootClass) { + mTaskRootPackage = taskRootPackage; + mTaskRootClass = taskRootClass; + } + } private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener = new UsageStatsManagerInternal.AppIdleStateChangeListener() { @@ -464,47 +472,57 @@ public class UsageStatsService extends SystemService implements final long elapsedRealtime = SystemClock.elapsedRealtime(); convertToSystemTimeLocked(event); - if (event.getPackageName() != null - && mPackageManagerInternal.isPackageEphemeral(userId, event.getPackageName())) { + if (event.mPackage != null + && mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) { event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP; } - final UserUsageStatsService service = - getUserDataAndInitializeIfNeededLocked(userId, timeNow); - service.reportEvent(event); - - mAppStandby.reportEvent(event, elapsedRealtime, userId); - - String packageName; - - switch(mUsageSource) { - case USAGE_SOURCE_CURRENT_ACTIVITY: - packageName = event.getPackageName(); - break; - case USAGE_SOURCE_TASK_ROOT_ACTIVITY: - default: - packageName = event.getTaskRootPackageName(); - if (packageName == null) { - packageName = event.getPackageName(); - } - break; - } - switch (event.mEventType) { case Event.ACTIVITY_RESUMED: - synchronized (mVisibleActivities) { - // check if this activity has already been resumed - if (mVisibleActivities.get(event.mInstanceId) != null) break; - mVisibleActivities.put(event.mInstanceId, event.getClassName()); - try { - mAppTimeLimit.noteUsageStart(packageName, userId); - } catch (IllegalArgumentException iae) { - Slog.e(TAG, "Failed to note usage start", iae); + // check if this activity has already been resumed + if (mVisibleActivities.get(event.mInstanceId) != null) break; + mVisibleActivities.put(event.mInstanceId, + new ActivityData(event.mTaskRootPackage, event.mTaskRootClass)); + try { + switch(mUsageSource) { + case USAGE_SOURCE_CURRENT_ACTIVITY: + mAppTimeLimit.noteUsageStart(event.mPackage, userId); + break; + case USAGE_SOURCE_TASK_ROOT_ACTIVITY: + default: + mAppTimeLimit.noteUsageStart(event.mTaskRootPackage, userId); + break; + } + } catch (IllegalArgumentException iae) { + Slog.e(TAG, "Failed to note usage start", iae); + } + break; + case Event.ACTIVITY_PAUSED: + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + final ActivityData prevData = mVisibleActivities.get(event.mInstanceId); + if (prevData == null) { + Slog.w(TAG, "Unexpected activity event reported! (" + event.mPackage + + "/" + event.mClass + " event : " + event.mEventType + + " instanceId : " + event.mInstanceId + ")"); + } else { + event.mTaskRootPackage = prevData.mTaskRootPackage; + event.mTaskRootClass = prevData.mTaskRootClass; } } break; - case Event.ACTIVITY_STOPPED: case Event.ACTIVITY_DESTROYED: + // Treat activity destroys like activity stops. + event.mEventType = Event.ACTIVITY_STOPPED; + // Fallthrough + case Event.ACTIVITY_STOPPED: + final ActivityData prevData = + mVisibleActivities.removeReturnOld(event.mInstanceId); + if (prevData == null) { + // The activity stop was already handled. + return; + } + ArraySet<String> tokens; synchronized (mUsageReporters) { tokens = mUsageReporters.removeReturnOld(event.mInstanceId); @@ -517,7 +535,7 @@ public class UsageStatsService extends SystemService implements final String token = tokens.valueAt(i); try { mAppTimeLimit.noteUsageStop( - buildFullToken(event.getPackageName(), token), userId); + buildFullToken(event.mPackage, token), userId); } catch (IllegalArgumentException iae) { Slog.w(TAG, "Failed to stop usage for during reporter death: " + iae); @@ -525,18 +543,32 @@ public class UsageStatsService extends SystemService implements } } } - - synchronized (mVisibleActivities) { - if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) { - try { - mAppTimeLimit.noteUsageStop(packageName, userId); - } catch (IllegalArgumentException iae) { - Slog.w(TAG, "Failed to note usage stop", iae); - } + if (event.mTaskRootPackage == null) { + // Task Root info is missing. Repair the event based on previous data + event.mTaskRootPackage = prevData.mTaskRootPackage; + event.mTaskRootClass = prevData.mTaskRootClass; + } + try { + switch(mUsageSource) { + case USAGE_SOURCE_CURRENT_ACTIVITY: + mAppTimeLimit.noteUsageStop(event.mPackage, userId); + break; + case USAGE_SOURCE_TASK_ROOT_ACTIVITY: + default: + mAppTimeLimit.noteUsageStop(event.mTaskRootPackage, userId); + break; } + } catch (IllegalArgumentException iae) { + Slog.w(TAG, "Failed to note usage stop", iae); } break; } + + final UserUsageStatsService service = + getUserDataAndInitializeIfNeededLocked(userId, timeNow); + service.reportEvent(event); + + mAppStandby.reportEvent(event, elapsedRealtime, userId); } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 93f758c2a9ad..b9440ebd88f6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1193,7 +1193,7 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public void setTranscription(IVoiceInteractionService service, String transcription) { + public void setUiHints(IVoiceInteractionService service, Bundle hints) { synchronized (this) { enforceIsCurrentVoiceInteractionService(service); @@ -1202,47 +1202,9 @@ public class VoiceInteractionManagerService extends SystemService { final IVoiceInteractionSessionListener listener = mVoiceInteractionSessionListeners.getBroadcastItem(i); try { - listener.onTranscriptionUpdate(transcription); + listener.onSetUiHints(hints); } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice transcription.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void clearTranscription(IVoiceInteractionService service, boolean immediate) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onTranscriptionComplete(immediate); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering transcription complete event.", e); - } - } - mVoiceInteractionSessionListeners.finishBroadcast(); - } - } - - @Override - public void setVoiceState(IVoiceInteractionService service, int state) { - synchronized (this) { - enforceIsCurrentVoiceInteractionService(service); - - final int size = mVoiceInteractionSessionListeners.beginBroadcast(); - for (int i = 0; i < size; ++i) { - final IVoiceInteractionSessionListener listener = - mVoiceInteractionSessionListeners.getBroadcastItem(i); - try { - listener.onVoiceStateChange(state); - } catch (RemoteException e) { - Slog.e(TAG, "Error delivering voice state change.", e); + Slog.e(TAG, "Error delivering UI hints.", e); } } mVoiceInteractionSessionListeners.finishBroadcast(); |