diff options
Diffstat (limited to 'services')
59 files changed, 2918 insertions, 856 deletions
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING new file mode 100644 index 000000000000..320924c954f8 --- /dev/null +++ b/services/accessibility/TEST_MAPPING @@ -0,0 +1,105 @@ +{ + "presubmit": [ + { + "name": "CtsAccessibilityServiceTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + }, + { + "name": "CtsAccessibilityTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + }, + { + "name": "CtsUiAutomationTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.accessibility" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.accessibility" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.view.accessibility" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsAccessibilityServiceTestCases" + }, + { + "name": "CtsAccessibilityTestCases" + }, + { + "name": "CtsUiAutomationTestCases" + }, + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.accessibility" + } + ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "com.android.internal.accessibility" + } + ] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.view.accessibility" + } + ] + } + ] +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 5b7332d0c636..bd4568568f6d 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -395,16 +395,42 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * or {@code null} when not found on either of them. */ @GuardedBy("mLock") - private AutofillValue findValueLocked(@NonNull AutofillId id) { - final ViewState state = mViewStates.get(id); + @Nullable + private AutofillValue findValueLocked(@NonNull AutofillId autofillId) { + final AutofillValue value = findValueFromThisSessionOnlyLocked(autofillId); + if (value != null) return value; + + // TODO(b/113281366): rather than explicitly look for previous session, it might be better + // to merge the sessions when created (see note on mergePreviousSessionLocked()) + final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this); + if (previousSessions != null) { + if (sDebug) { + Slog.d(TAG, "findValueLocked(): looking on " + previousSessions.size() + + " previous sessions for autofillId " + autofillId); + } + for (int i = 0; i < previousSessions.size(); i++) { + final Session previousSession = previousSessions.get(i); + final AutofillValue previousValue = previousSession + .findValueFromThisSessionOnlyLocked(autofillId); + if (previousValue != null) { + return previousValue; + } + } + } + return null; + } + + @Nullable + private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) { + final ViewState state = mViewStates.get(autofillId); if (state == null) { - if (sDebug) Slog.d(TAG, "findValueLocked(): no view state for " + id); + if (sDebug) Slog.d(TAG, "findValueLocked(): no view state for " + autofillId); return null; } AutofillValue value = state.getCurrentValue(); if (value == null) { - if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + id); - value = getValueFromContextsLocked(id); + if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + autofillId); + value = getValueFromContextsLocked(autofillId); } return value; } @@ -1769,15 +1795,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ @GuardedBy("mLock") @Nullable - private AutofillValue getValueFromContextsLocked(AutofillId id) { + private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id); + final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), + autofillId); if (node != null) { final AutofillValue value = node.getAutofillValue(); if (sDebug) { - Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + value); + Slog.d(TAG, "getValueFromContexts(" + autofillId + ") at " + i + ": " + value); } if (value != null && !value.isEmpty()) { return value; diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index bb97e4a97c97..e6cd7e0f6ebd 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -212,20 +212,20 @@ final class ViewState { public String toString() { final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id); if (mDatasetId != null) { - builder.append("datasetId:" ).append(mDatasetId); + builder.append(", datasetId:" ).append(mDatasetId); } builder.append("state:" ).append(getStateAsString()); if (mCurrentValue != null) { - builder.append("currentValue:" ).append(mCurrentValue); + builder.append(", currentValue:" ).append(mCurrentValue); } if (mAutofilledValue != null) { - builder.append("autofilledValue:" ).append(mAutofilledValue); + builder.append(", autofilledValue:" ).append(mAutofilledValue); } if (mSanitizedValue != null) { - builder.append("sanitizedValue:" ).append(mSanitizedValue); + builder.append(", sanitizedValue:" ).append(mSanitizedValue); } if (mVirtualBounds != null) { - builder.append("virtualBounds:" ).append(mVirtualBounds); + builder.append(", virtualBounds:" ).append(mVirtualBounds); } return builder.toString(); } diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java index 30ec8ab711b6..125c2250d7d1 100644 --- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java +++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java @@ -9,9 +9,9 @@ import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; -import android.app.backup.IBackupCallback; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; +import android.app.backup.IBackupCallback; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -21,6 +21,7 @@ import android.os.SELinux; import android.util.Slog; import com.android.internal.util.Preconditions; +import com.android.server.backup.fullbackup.AppMetadataBackupWriter; import com.android.server.backup.remote.ServiceBackupCallback; import com.android.server.backup.utils.FullBackupUtils; @@ -202,16 +203,20 @@ public class KeyValueAdbBackupEngine { public void run() { try { FullBackupDataOutput output = new FullBackupDataOutput(mPipe); + AppMetadataBackupWriter writer = + new AppMetadataBackupWriter(output, mPackageManager); if (DEBUG) { Slog.d(TAG, "Writing manifest for " + mPackage.packageName); } - FullBackupUtils.writeAppManifest( - mPackage, mPackageManager, mManifestFile, false, false); - FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null, - mDataDir.getAbsolutePath(), - mManifestFile.getAbsolutePath(), - output); + + writer.backupManifest( + mPackage, + mManifestFile, + mDataDir, + FullBackup.KEY_VALUE_DATA_TOKEN, + /* linkDomain */ null, + /* withApk */ false); mManifestFile.delete(); if (DEBUG) { diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java new file mode 100644 index 000000000000..94365d7dc02d --- /dev/null +++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java @@ -0,0 +1,283 @@ +package com.android.server.backup.fullbackup; + +import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION; +import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION; +import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; +import static com.android.server.backup.BackupManagerService.MORE_DEBUG; +import static com.android.server.backup.BackupManagerService.TAG; + +import android.annotation.Nullable; +import android.app.backup.FullBackup; +import android.app.backup.FullBackupDataOutput; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.os.Build; +import android.os.Environment; +import android.os.UserHandle; +import android.util.Log; +import android.util.StringBuilderPrinter; + +import com.android.internal.util.Preconditions; + +import java.io.BufferedOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +/** + * Writes the backup of app-specific metadata to {@link FullBackupDataOutput}. This data is not + * backed up by the app's backup agent and is written before the agent writes its own data. This + * includes the app's: + * + * <ul> + * <li>manifest + * <li>widget data + * <li>apk + * <li>obb content + * </ul> + */ +// TODO(b/113807190): Fix or remove apk and obb implementation (only used for adb). +public class AppMetadataBackupWriter { + private final FullBackupDataOutput mOutput; + private final PackageManager mPackageManager; + + /** The destination of the backup is specified by {@code output}. */ + public AppMetadataBackupWriter(FullBackupDataOutput output, PackageManager packageManager) { + mOutput = output; + mPackageManager = packageManager; + } + + /** + * Back up the app's manifest without specifying a pseudo-directory for the TAR stream. + * + * @see #backupManifest(PackageInfo, File, File, String, String, boolean) + */ + public void backupManifest( + PackageInfo packageInfo, File manifestFile, File filesDir, boolean withApk) + throws IOException { + backupManifest( + packageInfo, + manifestFile, + filesDir, + /* domain */ null, + /* linkDomain */ null, + withApk); + } + + /** + * Back up the app's manifest. + * + * <ol> + * <li>Write the app's manifest data to the specified temporary file {@code manifestFile}. + * <li>Backup the file in TAR format to the backup destination {@link #mOutput}. + * </ol> + * + * <p>Note: {@code domain} and {@code linkDomain} are only used by adb to specify a + * pseudo-directory for the TAR stream. + */ + // TODO(b/113806991): Look into streaming the backup data directly. + public void backupManifest( + PackageInfo packageInfo, + File manifestFile, + File filesDir, + @Nullable String domain, + @Nullable String linkDomain, + boolean withApk) + throws IOException { + byte[] manifestBytes = getManifestBytes(packageInfo, withApk); + FileOutputStream outputStream = new FileOutputStream(manifestFile); + outputStream.write(manifestBytes); + outputStream.close(); + + // We want the manifest block in the archive stream to be constant each time we generate + // a backup stream for the app. However, the underlying TAR mechanism sees it as a file and + // will propagate its last modified time. We pin the last modified time to zero to prevent + // the TAR header from varying. + manifestFile.setLastModified(0); + + FullBackup.backupToTar( + packageInfo.packageName, + domain, + linkDomain, + filesDir.getAbsolutePath(), + manifestFile.getAbsolutePath(), + mOutput); + } + + /** + * Gets the app's manifest as a byte array. All data are strings ending in LF. + * + * <p>The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + private byte[] getManifestBytes(PackageInfo packageInfo, boolean withApk) { + String packageName = packageInfo.packageName; + StringBuilder builder = new StringBuilder(4096); + StringBuilderPrinter printer = new StringBuilderPrinter(builder); + + printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); + printer.println(packageName); + printer.println(Long.toString(packageInfo.getLongVersionCode())); + printer.println(Integer.toString(Build.VERSION.SDK_INT)); + + String installerName = mPackageManager.getInstallerPackageName(packageName); + printer.println((installerName != null) ? installerName : ""); + + printer.println(withApk ? "1" : "0"); + + // Write the signature block. + SigningInfo signingInfo = packageInfo.signingInfo; + if (signingInfo == null) { + printer.println("0"); + } else { + // Retrieve the newest signatures to write. + // TODO (b/73988180) use entire signing history in case of rollbacks. + Signature[] signatures = signingInfo.getApkContentsSigners(); + printer.println(Integer.toString(signatures.length)); + for (Signature sig : signatures) { + printer.println(sig.toCharsString()); + } + } + return builder.toString().getBytes(); + } + + /** + * Backup specified widget data. The widget data is prefaced by a metadata header. + * + * <ol> + * <li>Write a metadata header to the specified temporary file {@code metadataFile}. + * <li>Write widget data bytes to the same file. + * <li>Backup the file in TAR format to the backup destination {@link #mOutput}. + * </ol> + * + * @throws IllegalArgumentException if the widget data provided is empty. + */ + // TODO(b/113806991): Look into streaming the backup data directly. + public void backupWidget( + PackageInfo packageInfo, File metadataFile, File filesDir, byte[] widgetData) + throws IOException { + Preconditions.checkArgument(widgetData.length > 0, "Can't backup widget with no data."); + + String packageName = packageInfo.packageName; + FileOutputStream fileOutputStream = new FileOutputStream(metadataFile); + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); + DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream); + + byte[] metadata = getMetadataBytes(packageName); + bufferedOutputStream.write(metadata); // bypassing DataOutputStream + writeWidgetData(dataOutputStream, widgetData); + bufferedOutputStream.flush(); + dataOutputStream.close(); + + // As with the manifest file, guarantee consistency of the archive metadata for the widget + // block by using a fixed last modified time on the metadata file. + metadataFile.setLastModified(0); + + FullBackup.backupToTar( + packageName, + /* domain */ null, + /* linkDomain */ null, + filesDir.getAbsolutePath(), + metadataFile.getAbsolutePath(), + mOutput); + } + + /** + * Gets the app's metadata as a byte array. All entries are strings ending in LF. + * + * <p>The metadata format is: + * + * <pre> + * BACKUP_METADATA_VERSION + * package name + * </pre> + */ + private byte[] getMetadataBytes(String packageName) { + StringBuilder builder = new StringBuilder(512); + StringBuilderPrinter printer = new StringBuilderPrinter(builder); + printer.println(Integer.toString(BACKUP_METADATA_VERSION)); + printer.println(packageName); + return builder.toString().getBytes(); + } + + /** + * Write a byte array of widget data to the specified output stream. All integers are binary in + * network byte order. + * + * <p>The widget data format: + * + * <pre> + * 4 : Integer token identifying the widget data blob. + * 4 : Integer size of the widget data. + * N : Raw bytes of the widget data. + * </pre> + */ + private void writeWidgetData(DataOutputStream out, byte[] widgetData) throws IOException { + out.writeInt(BACKUP_WIDGET_METADATA_TOKEN); + out.writeInt(widgetData.length); + out.write(widgetData); + } + + /** + * Backup the app's .apk to the backup destination {@link #mOutput}. Currently only used for + * 'adb backup'. + */ + // TODO(b/113807190): Investigate and potentially remove. + public void backupApk(PackageInfo packageInfo) { + // TODO: handle backing up split APKs + String appSourceDir = packageInfo.applicationInfo.getBaseCodePath(); + String apkDir = new File(appSourceDir).getParent(); + FullBackup.backupToTar( + packageInfo.packageName, + FullBackup.APK_TREE_TOKEN, + /* linkDomain */ null, + apkDir, + appSourceDir, + mOutput); + } + + /** + * Backup the app's .obb files to the backup destination {@link #mOutput}. Currently only used + * for 'adb backup'. + */ + // TODO(b/113807190): Investigate and potentially remove. + public void backupObb(PackageInfo packageInfo) { + // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM doesn't have access to + // external storage. + // TODO: http://b/22388012 + Environment.UserEnvironment userEnv = + new Environment.UserEnvironment(UserHandle.USER_SYSTEM); + File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0]; + if (obbDir != null) { + if (MORE_DEBUG) { + Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); + } + File[] obbFiles = obbDir.listFiles(); + if (obbFiles != null) { + String obbDirName = obbDir.getAbsolutePath(); + for (File obb : obbFiles) { + FullBackup.backupToTar( + packageInfo.packageName, + FullBackup.OBB_TREE_TOKEN, + /* linkDomain */ null, + obbDirName, + obb.getAbsolutePath(), + mOutput); + } + } + } + } +} diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java index 16906f74c8a8..c9f72181bcaf 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java +++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java @@ -18,8 +18,6 @@ package com.android.server.backup.fullbackup; import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME; import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME; -import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION; -import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; import static com.android.server.backup.BackupManagerService.DEBUG; import static com.android.server.backup.BackupManagerService.MORE_DEBUG; import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT; @@ -29,17 +27,14 @@ import static com.android.server.backup.BackupManagerService.TAG; import android.app.ApplicationThreadConstants; import android.app.IBackupAgent; import android.app.backup.BackupTransport; -import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.os.Environment.UserEnvironment; +import android.content.pm.PackageManager; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.UserHandle; -import android.util.Log; import android.util.Slog; -import android.util.StringBuilderPrinter; import com.android.internal.util.Preconditions; import com.android.server.AppWidgetBackupBridge; @@ -49,27 +44,20 @@ import com.android.server.backup.BackupRestoreTask; import com.android.server.backup.remote.RemoteCall; import com.android.server.backup.utils.FullBackupUtils; -import java.io.BufferedOutputStream; -import java.io.DataOutputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; /** - * Core logic for performing one package's full backup, gathering the tarball from the - * application and emitting it to the designated OutputStream. + * Core logic for performing one package's full backup, gathering the tarball from the application + * and emitting it to the designated OutputStream. */ public class FullBackupEngine { - private BackupManagerService backupManagerService; OutputStream mOutput; FullBackupPreflight mPreflightHook; BackupRestoreTask mTimeoutMonitor; IBackupAgent mAgent; - File mFilesDir; - File mManifestFile; - File mMetadataFile; boolean mIncludeApks; PackageInfo mPkg; private final long mQuota; @@ -78,79 +66,91 @@ public class FullBackupEngine { private final BackupAgentTimeoutParameters mAgentTimeoutParameters; class FullBackupRunner implements Runnable { - - PackageInfo mPackage; - byte[] mWidgetData; - IBackupAgent mAgent; - ParcelFileDescriptor mPipe; - int mToken; - boolean mSendApk; - boolean mWriteManifest; - - FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe, - int token, boolean sendApk, boolean writeManifest, byte[] widgetData) + private final PackageManager mPackageManager; + private final PackageInfo mPackage; + private final IBackupAgent mAgent; + private final ParcelFileDescriptor mPipe; + private final int mToken; + private final boolean mIncludeApks; + private final File mFilesDir; + + FullBackupRunner( + PackageInfo packageInfo, + IBackupAgent agent, + ParcelFileDescriptor pipe, + int token, + boolean includeApks) throws IOException { - mPackage = pack; - mWidgetData = widgetData; + mPackageManager = backupManagerService.getPackageManager(); + mPackage = packageInfo; mAgent = agent; mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor()); mToken = token; - mSendApk = sendApk; - mWriteManifest = writeManifest; + mIncludeApks = includeApks; + mFilesDir = new File("/data/system"); } @Override public void run() { try { - FullBackupDataOutput output = new FullBackupDataOutput( - mPipe, -1, mTransportFlags); + FullBackupDataOutput output = + new FullBackupDataOutput(mPipe, /* quota */ -1, mTransportFlags); + AppMetadataBackupWriter appMetadataBackupWriter = + new AppMetadataBackupWriter(output, mPackageManager); + + String packageName = mPackage.packageName; + boolean isSharedStorage = SHARED_BACKUP_AGENT_PACKAGE.equals(packageName); + boolean writeApk = + shouldWriteApk(mPackage.applicationInfo, mIncludeApks, isSharedStorage); - if (mWriteManifest) { - final boolean writeWidgetData = mWidgetData != null; + if (!isSharedStorage) { if (MORE_DEBUG) { - Slog.d(TAG, "Writing manifest for " + mPackage.packageName); + Slog.d(TAG, "Writing manifest for " + packageName); } - FullBackupUtils - .writeAppManifest(mPackage, backupManagerService.getPackageManager(), - mManifestFile, mSendApk, - writeWidgetData); - FullBackup.backupToTar(mPackage.packageName, null, null, - mFilesDir.getAbsolutePath(), - mManifestFile.getAbsolutePath(), - output); - mManifestFile.delete(); - // We only need to write a metadata file if we have widget data to stash - if (writeWidgetData) { - writeMetadata(mPackage, mMetadataFile, mWidgetData); - FullBackup.backupToTar(mPackage.packageName, null, null, - mFilesDir.getAbsolutePath(), - mMetadataFile.getAbsolutePath(), - output); - mMetadataFile.delete(); + File manifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME); + appMetadataBackupWriter.backupManifest( + mPackage, manifestFile, mFilesDir, writeApk); + manifestFile.delete(); + + // Write widget data. + // TODO: http://b/22388012 + byte[] widgetData = + AppWidgetBackupBridge.getWidgetState( + packageName, UserHandle.USER_SYSTEM); + if (widgetData != null && widgetData.length > 0) { + File metadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME); + appMetadataBackupWriter.backupWidget( + mPackage, metadataFile, mFilesDir, widgetData); + metadataFile.delete(); } } - if (mSendApk) { - writeApkToBackup(mPackage, output); + // TODO(b/113807190): Look into removing, only used for 'adb backup'. + if (writeApk) { + appMetadataBackupWriter.backupApk(mPackage); + appMetadataBackupWriter.backupObb(mPackage); } - final boolean isSharedStorage = - mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); - final long timeout = isSharedStorage ? - mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() : - mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); - if (DEBUG) { - Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName); + Slog.d(TAG, "Calling doFullBackup() on " + packageName); } - backupManagerService - .prepareOperationTimeout(mToken, - timeout, - mTimeoutMonitor /* in parent class */, - OP_TYPE_BACKUP_WAIT); - mAgent.doFullBackup(mPipe, mQuota, mToken, - backupManagerService.getBackupManagerBinder(), mTransportFlags); + + long timeout = + isSharedStorage + ? mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() + : mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); + backupManagerService.prepareOperationTimeout( + mToken, + timeout, + mTimeoutMonitor /* in parent class */, + OP_TYPE_BACKUP_WAIT); + mAgent.doFullBackup( + mPipe, + mQuota, + mToken, + backupManagerService.getBackupManagerBinder(), + mTransportFlags); } catch (IOException e) { Slog.e(TAG, "Error running full backup for " + mPackage.packageName); } catch (RemoteException e) { @@ -162,12 +162,33 @@ public class FullBackupEngine { } } } + + /** + * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded. + */ + private boolean shouldWriteApk( + ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) { + boolean isForwardLocked = + (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0; + boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + boolean isUpdatedSystemApp = + (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + return includeApks + && !isSharedStorage + && !isForwardLocked + && (!isSystemApp || isUpdatedSystemApp); + } } - public FullBackupEngine(BackupManagerService backupManagerService, + public FullBackupEngine( + BackupManagerService backupManagerService, OutputStream output, - FullBackupPreflight preflightHook, PackageInfo pkg, - boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota, int opToken, + FullBackupPreflight preflightHook, + PackageInfo pkg, + boolean alsoApks, + BackupRestoreTask timeoutMonitor, + long quota, + int opToken, int transportFlags) { this.backupManagerService = backupManagerService; mOutput = output; @@ -175,15 +196,13 @@ public class FullBackupEngine { mPkg = pkg; mIncludeApks = alsoApks; mTimeoutMonitor = timeoutMonitor; - mFilesDir = new File("/data/system"); - mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME); - mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME); mQuota = quota; mOpToken = opToken; mTransportFlags = transportFlags; - mAgentTimeoutParameters = Preconditions.checkNotNull( - backupManagerService.getAgentTimeoutParameters(), - "Timeout parameters cannot be null"); + mAgentTimeoutParameters = + Preconditions.checkNotNull( + backupManagerService.getAgentTimeoutParameters(), + "Timeout parameters cannot be null"); } public int preflightCheck() throws RemoteException { @@ -213,27 +232,13 @@ public class FullBackupEngine { try { pipes = ParcelFileDescriptor.createPipe(); - ApplicationInfo app = mPkg.applicationInfo; - final boolean isSharedStorage = - mPkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE); - final boolean sendApk = mIncludeApks - && !isSharedStorage - && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0) - && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 || - (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0); - - // TODO: http://b/22388012 - byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName, - UserHandle.USER_SYSTEM); - - FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1], - mOpToken, sendApk, !isSharedStorage, widgetBlob); - pipes[1].close(); // the runner has dup'd it + FullBackupRunner runner = + new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks); + pipes[1].close(); // the runner has dup'd it pipes[1] = null; Thread t = new Thread(runner, "app-data-runner"); t.start(); - // Now pull data from the app and stuff it into the output FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput); if (!backupManagerService.waitUntilOperationComplete(mOpToken)) { @@ -288,84 +293,13 @@ public class FullBackupEngine { if (MORE_DEBUG) { Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName); } - mAgent = backupManagerService.bindToAgentSynchronous(mPkg.applicationInfo, - ApplicationThreadConstants.BACKUP_MODE_FULL); + mAgent = + backupManagerService.bindToAgentSynchronous( + mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL); } return mAgent != null; } - private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) { - // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here - // TODO: handle backing up split APKs - final String appSourceDir = pkg.applicationInfo.getBaseCodePath(); - final String apkDir = new File(appSourceDir).getParent(); - FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null, - apkDir, appSourceDir, output); - - // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM - // doesn't have access to external storage. - - // Save associated .obb content if it exists and we did save the apk - // check for .obb and save those too - // TODO: http://b/22388012 - final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM); - final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0]; - if (obbDir != null) { - if (MORE_DEBUG) { - Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath()); - } - File[] obbFiles = obbDir.listFiles(); - if (obbFiles != null) { - final String obbDirName = obbDir.getAbsolutePath(); - for (File obb : obbFiles) { - FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null, - obbDirName, obb.getAbsolutePath(), output); - } - } - } - } - - // Widget metadata format. All header entries are strings ending in LF: - // - // Version 1 header: - // BACKUP_METADATA_VERSION, currently "1" - // package name - // - // File data (all integers are binary in network byte order) - // *N: 4 : integer token identifying which metadata blob - // 4 : integer size of this blob = N - // N : raw bytes of this metadata blob - // - // Currently understood blobs (always in network byte order): - // - // widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN) - // - // Unrecognized blobs are *ignored*, not errors. - private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData) - throws IOException { - StringBuilder b = new StringBuilder(512); - StringBuilderPrinter printer = new StringBuilderPrinter(b); - printer.println(Integer.toString(BACKUP_METADATA_VERSION)); - printer.println(pkg.packageName); - - FileOutputStream fout = new FileOutputStream(destination); - BufferedOutputStream bout = new BufferedOutputStream(fout); - DataOutputStream out = new DataOutputStream(bout); - bout.write(b.toString().getBytes()); // bypassing DataOutputStream - - if (widgetData != null && widgetData.length > 0) { - out.writeInt(BACKUP_WIDGET_METADATA_TOKEN); - out.writeInt(widgetData.length); - out.write(widgetData); - } - bout.flush(); - out.close(); - - // As with the manifest file, guarantee idempotence of the archive metadata - // for the widget block by using a fixed mtime on the transient file. - destination.setLastModified(0); - } - private void tearDown() { if (mPkg != null) { backupManagerService.tearDownAgentAndKill(mPkg.applicationInfo); diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java index a3d56011272f..dbe3cd9225b5 100644 --- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java +++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java @@ -16,23 +16,14 @@ package com.android.server.backup.utils; -import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION; import static com.android.server.backup.BackupManagerService.TAG; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.content.pm.SigningInfo; -import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; -import android.util.StringBuilderPrinter; import java.io.DataInputStream; import java.io.EOFException; -import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; @@ -68,67 +59,4 @@ public class FullBackupUtils { } } } - - /** - * Writes app manifest to the given manifest file. - * - * @param pkg - app package, which manifest to write. - * @param packageManager - {@link PackageManager} instance. - * @param manifestFile - target manifest file. - * @param withApk - whether include apk or not. - * @param withWidgets - whether to write widgets data. - * @throws IOException - in case of an error. - */ - // TODO: withWidgets is not used, decide whether it is needed. - public static void writeAppManifest(PackageInfo pkg, PackageManager packageManager, - File manifestFile, boolean withApk, boolean withWidgets) throws IOException { - // Manifest format. All data are strings ending in LF: - // BACKUP_MANIFEST_VERSION, currently 1 - // - // Version 1: - // package name - // package's versionCode - // platform versionCode - // getInstallerPackageName() for this package (maybe empty) - // boolean: "1" if archive includes .apk; any other string means not - // number of signatures == N - // N*: signature byte array in ascii format per Signature.toCharsString() - StringBuilder builder = new StringBuilder(4096); - StringBuilderPrinter printer = new StringBuilderPrinter(builder); - - printer.println(Integer.toString(BACKUP_MANIFEST_VERSION)); - printer.println(pkg.packageName); - printer.println(Long.toString(pkg.getLongVersionCode())); - printer.println(Integer.toString(Build.VERSION.SDK_INT)); - - String installerName = packageManager.getInstallerPackageName(pkg.packageName); - printer.println((installerName != null) ? installerName : ""); - - printer.println(withApk ? "1" : "0"); - - // write the signature block - SigningInfo signingInfo = pkg.signingInfo; - if (signingInfo == null) { - printer.println("0"); - } else { - // retrieve the newest sigs to write - // TODO (b/73988180) use entire signing history in case of rollbacks - Signature[] signatures = signingInfo.getApkContentsSigners(); - printer.println(Integer.toString(signatures.length)); - for (Signature sig : signatures) { - printer.println(sig.toCharsString()); - } - } - - FileOutputStream outstream = new FileOutputStream(manifestFile); - outstream.write(builder.toString().getBytes()); - outstream.close(); - - // We want the manifest block in the archive stream to be idempotent: - // each time we generate a backup stream for the app, we want the manifest - // block to be identical. The underlying tar mechanism sees it as a file, - // though, and will propagate its mtime, causing the tar header to vary. - // Avoid this problem by pinning the mtime to zero. - manifestFile.setLastModified(0); - } } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 26ef42f2b1e7..499c03d05bf2 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -52,6 +52,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.os.Message; import android.os.ParcelableException; import android.os.PowerManager; @@ -111,7 +112,7 @@ import java.util.TreeSet; import java.util.function.Predicate; /** - * Alarm manager implementaion. + * Alarm manager implementation. * * Unit test: atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -138,7 +139,6 @@ class AlarmManagerService extends SystemService { static final boolean DEBUG_STANDBY = localLOGV || false; static final boolean RECORD_ALARMS_IN_HISTORY = true; static final boolean RECORD_DEVICE_IDLE_ALARMS = false; - static final int ALARM_EVENT = 1; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays @@ -169,7 +169,6 @@ class AlarmManagerService extends SystemService { // List of alarms per uid deferred due to user applied background restrictions on the source app SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>(); - long mNativeData; private long mNextWakeup; private long mNextNonWakeup; private long mNextWakeUpSetAt; @@ -181,15 +180,14 @@ class AlarmManagerService extends SystemService { private long mLastTickReceived; private long mLastTickAdded; private long mLastTickRemoved; + private final Injector mInjector; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); - final AlarmHandler mHandler = new AlarmHandler(); + AlarmHandler mHandler; ClockReceiver mClockReceiver; - InteractiveStateReceiver mInteractiveStateReceiver; - private UninstallReceiver mUninstallReceiver; final DeliveryTracker mDeliveryTracker = new DeliveryTracker(); PendingIntent mTimeTickSender; PendingIntent mDateChangeSender; @@ -277,7 +275,8 @@ class AlarmManagerService extends SystemService { * global Settings. Any access to this class or its fields should be done while * holding the AlarmManagerService.mLock lock. */ - private final class Constants extends ContentObserver { + @VisibleForTesting + final class Constants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_MIN_FUTURITY = "min_futurity"; private static final String KEY_MIN_INTERVAL = "min_interval"; @@ -456,7 +455,7 @@ class AlarmManagerService extends SystemService { } } - final Constants mConstants; + Constants mConstants; // Alarm delivery ordering bookkeeping static final int PRIO_TICK = 0; @@ -510,7 +509,7 @@ class AlarmManagerService extends SystemService { flags = seed.flags; alarms.add(seed); if (seed.operation == mTimeTickSender) { - mLastTickAdded = System.currentTimeMillis(); + mLastTickAdded = mInjector.getCurrentTimeMillis(); } } @@ -535,7 +534,7 @@ class AlarmManagerService extends SystemService { } alarms.add(index, alarm); if (alarm.operation == mTimeTickSender) { - mLastTickAdded = System.currentTimeMillis(); + mLastTickAdded = mInjector.getCurrentTimeMillis(); } if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); @@ -573,7 +572,7 @@ class AlarmManagerService extends SystemService { mNextAlarmClockMayChange = true; } if (alarm.operation == mTimeTickSender) { - mLastTickRemoved = System.currentTimeMillis(); + mLastTickRemoved = mInjector.getCurrentTimeMillis(); } } else { if (alarm.whenElapsed > newStart) { @@ -734,17 +733,20 @@ class AlarmManagerService extends SystemService { Alarm mNextWakeFromIdle = null; ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>(); - public AlarmManagerService(Context context) { + @VisibleForTesting + AlarmManagerService(Context context, Injector injector) { super(context); - mConstants = new Constants(mHandler); + mInjector = injector; + } - publishLocalService(AlarmManagerInternal.class, new LocalService()); + AlarmManagerService(Context context) { + this(context, new Injector(context)); } - static long convertToElapsed(long when, int type) { + private long convertToElapsed(long when, int type) { final boolean isRtc = (type == RTC || type == RTC_WAKEUP); if (isRtc) { - when -= System.currentTimeMillis() - SystemClock.elapsedRealtime(); + when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime(); } return when; } @@ -854,7 +856,7 @@ class AlarmManagerService extends SystemService { ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone(); mAlarmBatches.clear(); Alarm oldPendingIdleUntil = mPendingIdleUntil; - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final int oldBatches = oldSet.size(); for (int batchNum = 0; batchNum < oldBatches; batchNum++) { Batch batch = oldSet.get(batchNum); @@ -984,7 +986,7 @@ class AlarmManagerService extends SystemService { alarmsToDeliver = alarmsForUid; mPendingBackgroundAlarms.remove(uid); } - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); } /** @@ -1001,7 +1003,7 @@ class AlarmManagerService extends SystemService { mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted); if (alarmsToDeliver.size() > 0) { - deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime()); + deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime()); } } @@ -1088,7 +1090,7 @@ class AlarmManagerService extends SystemService { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0; ent.pkg = "FINISH IDLE"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } @@ -1096,7 +1098,7 @@ class AlarmManagerService extends SystemService { if (mPendingWhileIdleAlarms.size() > 0) { ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; mPendingWhileIdleAlarms = new ArrayList<>(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); for (int i=alarms.size() - 1; i >= 0; i--) { Alarm a = alarms.get(i); reAddAlarmLocked(a, nowElapsed, false); @@ -1282,121 +1284,114 @@ class AlarmManagerService extends SystemService { @Override public void onStart() { - mNativeData = init(); - mNextWakeup = mNextNonWakeup = 0; + mInjector.init(); + + synchronized (mLock) { + mHandler = new AlarmHandler(Looper.myLooper()); + mConstants = new Constants(mHandler); - // We have to set current TimeZone info to kernel - // because kernel doesn't keep this after reboot - setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); + mNextWakeup = mNextNonWakeup = 0; - // Also sure that we're booting with a halfway sensible current time - if (mNativeData != 0) { + // We have to set current TimeZone info to kernel + // because kernel doesn't keep this after reboot + setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); + + // Also sure that we're booting with a halfway sensible current time final long systemBuildTime = Environment.getRootDirectory().lastModified(); - if (System.currentTimeMillis() < systemBuildTime) { - Slog.i(TAG, "Current time only " + System.currentTimeMillis() + if (mInjector.getCurrentTimeMillis() < systemBuildTime) { + Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis() + ", advancing to build time " + systemBuildTime); - setKernelTime(mNativeData, systemBuildTime); + mInjector.setKernelTime(systemBuildTime); } - } - // Determine SysUI's uid - final PackageManager packMan = getContext().getPackageManager(); - try { - PermissionInfo sysUiPerm = packMan.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0); - ApplicationInfo sysUi = packMan.getApplicationInfo(sysUiPerm.packageName, 0); - if ((sysUi.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { - mSystemUiUid = sysUi.uid; + // Determine SysUI's uid + mSystemUiUid = mInjector.getSystemUiUid(); + if (mSystemUiUid <= 0) { + Slog.wtf(TAG, "SysUI package not found!"); + } + mWakeLock = mInjector.getAlarmWakeLock(); + + mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, + new Intent(Intent.ACTION_TIME_TICK).addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, + UserHandle.ALL); + Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); + mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, + Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); + + mClockReceiver = mInjector.getClockReceiver(this); + new InteractiveStateReceiver(); + new UninstallReceiver(); + + if (mInjector.isAlarmDriverPresent()) { + AlarmThread waitThread = new AlarmThread(); + waitThread.start(); } else { - Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION - + " defined by non-privileged app " + sysUi.packageName - + " - ignoring"); - } - } catch (NameNotFoundException e) { - } - - if (mSystemUiUid <= 0) { - Slog.wtf(TAG, "SysUI package not found!"); - } - - PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*"); - - mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, - new Intent(Intent.ACTION_TIME_TICK).addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, - UserHandle.ALL); - Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING - | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); - mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, - Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); - - // now that we have initied the driver schedule the alarm - mClockReceiver = new ClockReceiver(); - mClockReceiver.scheduleTimeTickEvent(); - mClockReceiver.scheduleDateChangedEvent(); - mInteractiveStateReceiver = new InteractiveStateReceiver(); - mUninstallReceiver = new UninstallReceiver(); - - if (mNativeData != 0) { - AlarmThread waitThread = new AlarmThread(); - waitThread.start(); - } else { - Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); - } + Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); + } - try { - ActivityManager.getService().registerUidObserver(new UidObserver(), - ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE - | ActivityManager.UID_OBSERVER_ACTIVE, - ActivityManager.PROCESS_STATE_UNKNOWN, null); - } catch (RemoteException e) { - // ignored; both services live in system_server + try { + ActivityManager.getService().registerUidObserver(new UidObserver(), + ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE + | ActivityManager.UID_OBSERVER_ACTIVE, + ActivityManager.PROCESS_STATE_UNKNOWN, null); + } catch (RemoteException e) { + // ignored; both services live in system_server + } } + publishLocalService(AlarmManagerInternal.class, new LocalService()); publishBinderService(Context.ALARM_SERVICE, mService); } @Override public void onBootPhase(int phase) { if (phase == PHASE_SYSTEM_SERVICES_READY) { - mConstants.start(getContext().getContentResolver()); - mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); - mLocalDeviceIdleController - = LocalServices.getService(DeviceIdleController.LocalService.class); - mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); - mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker()); + synchronized (mLock) { + mConstants.start(getContext().getContentResolver()); + mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); + mLocalDeviceIdleController = + LocalServices.getService(DeviceIdleController.LocalService.class); + mUsageStatsManagerInternal = + LocalServices.getService(UsageStatsManagerInternal.class); + mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker()); + + mAppStateTracker = LocalServices.getService(AppStateTracker.class); + mAppStateTracker.addListener(mForceAppStandbyListener); - mAppStateTracker = LocalServices.getService(AppStateTracker.class); - mAppStateTracker.addListener(mForceAppStandbyListener); + mClockReceiver.scheduleTimeTickEvent(); + mClockReceiver.scheduleDateChangedEvent(); + } } } @Override protected void finalize() throws Throwable { try { - close(mNativeData); + mInjector.close(); } finally { super.finalize(); } } boolean setTimeImpl(long millis) { - if (mNativeData == 0) { + if (!mInjector.isAlarmDriverPresent()) { Slog.w(TAG, "Not setting time since no alarm driver is available."); return false; } synchronized (mLock) { - final long currentTimeMillis = System.currentTimeMillis(); - setKernelTime(mNativeData, millis); + final long currentTimeMillis = mInjector.getCurrentTimeMillis(); + mInjector.setKernelTime(millis); final TimeZone timeZone = TimeZone.getDefault(); final int currentTzOffset = timeZone.getOffset(currentTimeMillis); final int newTzOffset = timeZone.getOffset(millis); if (currentTzOffset != newTzOffset) { Slog.i(TAG, "Timezone offset has changed, updating kernel timezone"); - setKernelTimezone(mNativeData, -(newTzOffset / 60000)); + mInjector.setKernelTimezone(-(newTzOffset / 60000)); } // The native implementation of setKernelTime can return -1 even when the kernel // time was set correctly, so assume setting kernel time was successful and always @@ -1426,8 +1421,8 @@ class AlarmManagerService extends SystemService { // Update the kernel timezone information // Kernel tracks time offsets as 'minutes west of GMT' - int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mNativeData, -(gmtOffset / 60000)); + int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis()); + mInjector.setKernelTimezone(-(gmtOffset / 60000)); } TimeZone.setDefault(null); @@ -1498,7 +1493,7 @@ class AlarmManagerService extends SystemService { triggerAtTime = 0; } - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final long nominalTrigger = convertToElapsed(triggerAtTime, type); // Try to prevent spamming by making sure we aren't firing alarms in the immediate future final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY; @@ -1551,7 +1546,8 @@ class AlarmManagerService extends SystemService { * Return the minimum time that should elapse before an app in the specified bucket * can receive alarms again */ - private long getMinDelayForBucketLocked(int bucket) { + @VisibleForTesting + long getMinDelayForBucketLocked(int bucket) { // UsageStats bucket values are treated as floors of their behavioral range. // In other words, a bucket value between WORKING and ACTIVE is treated as // WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific @@ -1591,7 +1587,7 @@ class AlarmManagerService extends SystemService { final String sourcePackage = alarm.sourcePackage; final int sourceUserId = UserHandle.getUserId(alarm.creatorUid); final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket( - sourcePackage, sourceUserId, SystemClock.elapsedRealtime()); + sourcePackage, sourceUserId, mInjector.getElapsedRealtime()); final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId); final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L); @@ -1619,7 +1615,7 @@ class AlarmManagerService extends SystemService { a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } // Add fuzz to make the alarm go off some time before the actual desired time. - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed); if (fuzz > 0) { if (mRandom == null) { @@ -1655,7 +1651,7 @@ class AlarmManagerService extends SystemService { ent.pkg = a.operation.getCreatorPackage(); ent.tag = a.operation.getTag(""); ent.op = "SET"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); ent.argRealtime = a.whenElapsed; mAllowWhileIdleDispatches.add(ent); } @@ -1675,7 +1671,7 @@ class AlarmManagerService extends SystemService { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = 0; ent.pkg = "START IDLE"; - ent.elapsedRealtime = SystemClock.elapsedRealtime(); + ent.elapsedRealtime = mInjector.getElapsedRealtime(); mAllowWhileIdleDispatches.add(ent); } } @@ -1890,8 +1886,8 @@ class AlarmManagerService extends SystemService { pw.println(" App Standby Parole: " + mAppStandbyParole); pw.println(); - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); final long nowUPTIME = SystemClock.uptimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -1958,10 +1954,10 @@ class AlarmManagerService extends SystemService { pw.println(); pw.print(" Next kernel non-wakeup alarm: "); - TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME), pw); + TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw); pw.println(); pw.print(" Next kernel wakeup alarm: "); - TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP), pw); + TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw); pw.println(); pw.print(" Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw); @@ -2257,8 +2253,8 @@ class AlarmManagerService extends SystemService { final ProtoOutputStream proto = new ProtoOutputStream(fd); synchronized (mLock) { - final long nowRTC = System.currentTimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtime(); proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC); proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed); proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME, @@ -2494,8 +2490,8 @@ class AlarmManagerService extends SystemService { private void logBatchesLocked(SimpleDateFormat sdf) { ByteArrayOutputStream bs = new ByteArrayOutputStream(2048); PrintWriter pw = new PrintWriter(bs); - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); final int NZ = mAlarmBatches.size(); for (int iz = 0; iz < NZ; iz++) { Batch bz = mAlarmBatches.get(iz); @@ -2693,7 +2689,7 @@ class AlarmManagerService extends SystemService { TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg); errorMsg.append(", mLastWakeup="); TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg); - errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME)); + errorMsg.append(", timerfd_gettime=" + mInjector.getNextAlarm(ELAPSED_REALTIME)); errorMsg.append("];"); } if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) { @@ -2705,7 +2701,7 @@ class AlarmManagerService extends SystemService { errorMsg.append(", mLastWakeup="); TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg); errorMsg.append(", timerfd_gettime=" - + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP)); + + mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP)); errorMsg.append("];"); } if (stuck) { @@ -2716,7 +2712,7 @@ class AlarmManagerService extends SystemService { void rescheduleKernelAlarmsLocked() { // Schedule the next upcoming wakeup alarm. If there is a deliverable batch // prior to that which contains no wakeups, we schedule that as well. - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtime(); validateLastAlarmExpiredLocked(nowElapsed); long nextNonWakeup = 0; if (mAlarmBatches.size() > 0) { @@ -2743,7 +2739,7 @@ class AlarmManagerService extends SystemService { } } - private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { + void removeLocked(PendingIntent operation, IAlarmListener directReceiver) { if (operation == null && directReceiver == null) { if (localLOGV) { Slog.w(TAG, "requested remove() of null operation", @@ -2983,7 +2979,7 @@ class AlarmManagerService extends SystemService { void interactiveStateChangedLocked(boolean interactive) { if (mInteractive != interactive) { mInteractive = interactive; - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtime(); if (interactive) { if (mPendingNonWakeupAlarms.size() > 0) { final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; @@ -3023,31 +3019,13 @@ class AlarmManagerService extends SystemService { } private void setLocked(int type, long when) { - if (mNativeData != 0) { - // The kernel never triggers alarms with negative wakeup times - // so we ensure they are positive. - long alarmSeconds, alarmNanoseconds; - if (when < 0) { - alarmSeconds = 0; - alarmNanoseconds = 0; - } else { - alarmSeconds = when / 1000; - alarmNanoseconds = (when % 1000) * 1000 * 1000; - } - - final int result = set(mNativeData, type, alarmSeconds, alarmNanoseconds); - if (result != 0) { - final long nowElapsed = SystemClock.elapsedRealtime(); - Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed - + " type=" + type + " when=" + when - + " @ (" + alarmSeconds + "," + alarmNanoseconds - + "), ret = " + result + " = " + Os.strerror(result)); - } + if (mInjector.isAlarmDriverPresent()) { + mInjector.setAlarm(type, when); } else { Message msg = Message.obtain(); - msg.what = ALARM_EVENT; + msg.what = AlarmHandler.ALARM_EVENT; - mHandler.removeMessages(ALARM_EVENT); + mHandler.removeMessages(msg.what); mHandler.sendMessageAtTime(msg, when); } } @@ -3106,13 +3084,13 @@ class AlarmManagerService extends SystemService { exemptOnBatterySaver); } - private native long init(); - private native void close(long nativeData); - private native int set(long nativeData, int type, long seconds, long nanoseconds); - private native int waitForAlarm(long nativeData); - private native int setKernelTime(long nativeData, long millis); - private native int setKernelTimezone(long nativeData, int minuteswest); - private native long getNextAlarm(long nativeData, int type); + private static native long init(); + private static native void close(long nativeData); + private static native int set(long nativeData, int type, long seconds, long nanoseconds); + private static native int waitForAlarm(long nativeData); + private static native int setKernelTime(long nativeData, long millis); + private static native int setKernelTimezone(long nativeData, int minuteswest); + private static native long getNextAlarm(long nativeData, int type); private long getWhileIdleMinIntervalLocked(int uid) { final boolean dozing = mPendingIdleUntil != null; @@ -3516,6 +3494,103 @@ class AlarmManagerService extends SystemService { || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0; } + @VisibleForTesting + static class Injector { + private long mNativeData; + private Context mContext; + + Injector(Context context) { + mContext = context; + } + + void init() { + mNativeData = AlarmManagerService.init(); + } + + int waitForAlarm() { + return AlarmManagerService.waitForAlarm(mNativeData); + } + + boolean isAlarmDriverPresent() { + return mNativeData != 0; + } + + void setAlarm(int type, long millis) { + // The kernel never triggers alarms with negative wakeup times + // so we ensure they are positive. + final long alarmSeconds, alarmNanoseconds; + if (millis < 0) { + alarmSeconds = 0; + alarmNanoseconds = 0; + } else { + alarmSeconds = millis / 1000; + alarmNanoseconds = (millis % 1000) * 1000 * 1000; + } + + final int result = AlarmManagerService.set(mNativeData, type, alarmSeconds, + alarmNanoseconds); + if (result != 0) { + final long nowElapsed = SystemClock.elapsedRealtime(); + Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed + + " type=" + type + " @ (" + alarmSeconds + "," + alarmNanoseconds + + "), ret = " + result + " = " + Os.strerror(result)); + } + } + + long getNextAlarm(int type) { + return AlarmManagerService.getNextAlarm(mNativeData, type); + } + + void setKernelTimezone(int minutesWest) { + AlarmManagerService.setKernelTimezone(mNativeData, minutesWest); + } + + void setKernelTime(long millis) { + if (mNativeData != 0) { + AlarmManagerService.setKernelTime(mNativeData, millis); + } + } + + void close() { + AlarmManagerService.close(mNativeData); + } + + long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + + long getCurrentTimeMillis() { + return System.currentTimeMillis(); + } + + PowerManager.WakeLock getAlarmWakeLock() { + final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); + return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*"); + } + + int getSystemUiUid() { + int sysUiUid = -1; + final PackageManager pm = mContext.getPackageManager(); + try { + PermissionInfo sysUiPerm = pm.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0); + ApplicationInfo sysUi = pm.getApplicationInfo(sysUiPerm.packageName, 0); + if ((sysUi.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { + sysUiUid = sysUi.uid; + } else { + Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION + + " defined by non-privileged app " + sysUi.packageName + + " - ignoring"); + } + } catch (NameNotFoundException e) { + } + return sysUiUid; + } + + ClockReceiver getClockReceiver(AlarmManagerService service) { + return service.new ClockReceiver(); + } + } + private class AlarmThread extends Thread { private int mFalseWakeups; @@ -3524,7 +3599,7 @@ class AlarmManagerService extends SystemService { { super("AlarmManager"); mFalseWakeups = 0; - mWtfThreshold = 10; + mWtfThreshold = 100; } public void run() @@ -3533,14 +3608,16 @@ class AlarmManagerService extends SystemService { while (true) { - int result = waitForAlarm(mNativeData); - - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + int result = mInjector.waitForAlarm(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); synchronized (mLock) { mLastWakeup = nowELAPSED; } - + if (result == 0) { + Slog.wtf(TAG, "waitForAlarm returned 0, nowRTC = " + nowRTC + + ", nowElapsed = " + nowELAPSED); + } triggerList.clear(); if ((result & TIME_CHANGED_MASK) != 0) { @@ -3720,7 +3797,8 @@ class AlarmManagerService extends SystemService { public static final int APP_STANDBY_PAROLE_CHANGED = 6; public static final int REMOVE_FOR_STOPPED = 7; - public AlarmHandler() { + AlarmHandler(Looper looper) { + super(looper); } public void postRemoveForStopped(int uid) { @@ -3732,8 +3810,8 @@ class AlarmManagerService extends SystemService { case ALARM_EVENT: { ArrayList<Alarm> triggerList = new ArrayList<Alarm>(); synchronized (mLock) { - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowRTC = mInjector.getCurrentTimeMillis(); + final long nowELAPSED = mInjector.getElapsedRealtime(); triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC); updateNextAlarmClockLocked(); } @@ -3817,7 +3895,7 @@ class AlarmManagerService extends SystemService { Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); } synchronized (mLock) { - mLastTickReceived = System.currentTimeMillis(); + mLastTickReceived = mInjector.getCurrentTimeMillis(); } scheduleTimeTickEvent(); } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { @@ -3826,14 +3904,14 @@ class AlarmManagerService extends SystemService { // based off of the current Zone gmt offset + userspace tracked // daylight savings information. TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY)); - int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mNativeData, -(gmtOffset / 60000)); + int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis()); + mInjector.setKernelTimezone(-(gmtOffset / 60000)); scheduleDateChangedEvent(); } } public void scheduleTimeTickEvent() { - final long currentTime = System.currentTimeMillis(); + final long currentTime = mInjector.getCurrentTimeMillis(); final long nextTime = 60000 * ((currentTime / 60000) + 1); // Schedule this event for the amount of time that it would take to get to @@ -3841,7 +3919,7 @@ class AlarmManagerService extends SystemService { final long tickEventDelay = nextTime - currentTime; final WorkSource workSource = null; // Let system take blame for time tick events. - setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid(), "android"); @@ -3853,7 +3931,7 @@ class AlarmManagerService extends SystemService { public void scheduleDateChangedEvent() { Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); + calendar.setTimeInMillis(mInjector.getCurrentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); @@ -4122,7 +4200,7 @@ class AlarmManagerService extends SystemService { } private void updateStatsLocked(InFlight inflight) { - final long nowELAPSED = SystemClock.elapsedRealtime(); + final long nowELAPSED = mInjector.getElapsedRealtime(); BroadcastStats bs = inflight.mBroadcastStats; bs.nesting--; if (bs.nesting <= 0) { diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 50f15ca0739f..a85b69b528dd 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -399,6 +399,12 @@ public final class BatteryService extends SystemService { private void update(android.hardware.health.V2_0.HealthInfo info) { traceBegin("HealthInfoUpdate"); + + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter", + info.legacy.batteryChargeCounter); + Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent", + info.legacy.batteryCurrent); + synchronized (mLock) { if (!mUpdatesStopped) { mHealthInfo = info.legacy; diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 617e803e3f3b..f981b261c537 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -127,6 +127,7 @@ import android.widget.TextView; import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.inputmethod.IInputContentUriToken; +import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController; import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem; import com.android.internal.inputmethod.InputMethodUtils; @@ -198,7 +199,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_SHOW_SOFT_INPUT = 1020; static final int MSG_HIDE_SOFT_INPUT = 1030; static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035; - static final int MSG_ATTACH_TOKEN = 1040; + static final int MSG_INITIALIZE_IME = 1040; static final int MSG_CREATE_SESSION = 1050; static final int MSG_START_INPUT = 2000; @@ -1968,7 +1969,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO( - MSG_ATTACH_TOKEN, mCurMethod, mCurToken)); + MSG_INITIALIZE_IME, mCurMethod, mCurToken)); if (mCurClient != null) { clearClientSessionLocked(mCurClient); requestClientSessionLocked(mCurClient); @@ -2217,8 +2218,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @SuppressWarnings("deprecation") - @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + private void setImeWindowStatus(IBinder token, int vis, int backDisposition) { if (!calledWithValidToken(token)) { return; } @@ -2253,8 +2253,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @BinderThread - @Override - public void reportStartInput(IBinder token, IBinder startInputToken) { + private void reportStartInput(IBinder token, IBinder startInputToken) { if (!calledWithValidToken(token)) { return; } @@ -3158,8 +3157,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mWindowManagerInternal.getInputMethodWindowVisibleHeight(); } - @Override - public void clearLastInputMethodWindowForTransition(IBinder token) { + @BinderThread + private void clearLastInputMethodWindowForTransition(IBinder token) { if (!calledFromValidUser()) { return; } @@ -3354,11 +3353,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideCurrentInputLocked(0, null); } return true; - case MSG_ATTACH_TOKEN: + case MSG_INITIALIZE_IME: args = (SomeArgs)msg.obj; try { if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2); - ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2); + final IBinder token = (IBinder) args.arg2; + ((IInputMethod) args.arg1).initializeInternal(token, + new InputMethodPrivilegedOperationsImpl(this, token)); } catch (RemoteException e) { } args.recycle(); @@ -4432,8 +4433,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token, + @BinderThread + private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token, @Nullable Uri contentUri, @Nullable String packageName) { if (!calledFromValidUser()) { return null; @@ -4490,8 +4491,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public void reportFullscreenMode(IBinder token, boolean fullscreen) { + @BinderThread + private void reportFullscreenMode(IBinder token, boolean fullscreen) { if (!calledFromValidUser()) { return; } @@ -4966,4 +4967,45 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return ShellCommandResult.SUCCESS; } } + + private static final class InputMethodPrivilegedOperationsImpl + extends IInputMethodPrivilegedOperations.Stub { + private final InputMethodManagerService mImms; + private final IBinder mToken; + InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms, IBinder token) { + mImms = imms; + mToken = token; + } + + @BinderThread + @Override + public void setImeWindowStatus(int vis, int backDisposition) { + mImms.setImeWindowStatus(mToken, vis, backDisposition); + } + + @BinderThread + @Override + public void reportStartInput(IBinder startInputToken) { + mImms.reportStartInput(mToken, startInputToken); + } + + @BinderThread + @Override + public void clearLastInputMethodWindowForTransition() { + mImms.clearLastInputMethodWindowForTransition(mToken); + } + + @BinderThread + @Override + public IInputContentUriToken createInputContentUriToken(Uri contentUri, + String packageName) { + return mImms.createInputContentUriToken(mToken, contentUri, packageName); + } + + @BinderThread + @Override + public void reportFullscreenMode(boolean fullscreen) { + mImms.reportFullscreenMode(mToken, fullscreen); + } + } } diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index d8296023383c..8ec39d8a46b3 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -626,22 +626,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub mBandwidthControlEnabled = false; - // only enable bandwidth control when support exists - final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists(); - // push any existing quota or UID rules synchronized (mQuotaLock) { - if (hasKernelSupport) { - Slog.d(TAG, "enabling bandwidth control"); - try { - mConnector.execute("bandwidth", "enable"); - mBandwidthControlEnabled = true; - } catch (NativeDaemonConnectorException e) { - Log.wtf(TAG, "problem enabling bandwidth controls", e); - } - } else { - Slog.i(TAG, "not enabling bandwidth control"); + // TODO: Delete this code and have netd unconditionally enable bandwidth control at + // startup time + Slog.d(TAG, "enabling bandwidth control"); + try { + mConnector.execute("bandwidth", "enable"); + mBandwidthControlEnabled = true; + } catch (NativeDaemonConnectorException e) { + Log.wtf(TAG, "problem enabling bandwidth controls", e); } SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0"); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index c44a81e2dcd0..98b88cb557af 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -101,6 +101,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { IPhoneStateListener callback; IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback; + IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback; int callerUid; int callerPid; @@ -119,6 +120,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return (onSubscriptionsChangedListenerCallback != null); } + boolean matchOnOpportunisticSubscriptionsChangedListener() { + return (onOpportunisticSubscriptionsChangedListenerCallback != null); + } + boolean canReadCallLog() { try { return TelephonyPermissions.checkReadCallLog( @@ -133,7 +138,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return "{callingPackage=" + callingPackage + " binder=" + binder + " callback=" + callback + " onSubscriptionsChangedListenererCallback=" - + onSubscriptionsChangedListenerCallback + + onSubscriptionsChangedListenerCallback + + " onOpportunisticSubscriptionsChangedListenererCallback=" + + onOpportunisticSubscriptionsChangedListenerCallback + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId + " events=" + Integer.toHexString(events) + "}"; } @@ -149,7 +156,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private final AppOpsManager mAppOps; - private boolean hasNotifySubscriptionInfoChangedOccurred = false; + private boolean mHasNotifySubscriptionInfoChangedOccurred = false; + + private boolean mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = false; private int mNumPhones; @@ -410,7 +419,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("listen oscl: Register r=" + r); } // Always notify when registration occurs if there has been a notification. - if (hasNotifySubscriptionInfoChangedOccurred) { + if (mHasNotifySubscriptionInfoChangedOccurred) { try { if (VDBG) log("listen oscl: send to r=" + r); r.onSubscriptionsChangedListenerCallback.onSubscriptionsChanged(); @@ -420,7 +429,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } else { - log("listen oscl: hasNotifySubscriptionInfoChangedOccurred==false no callback"); + log("listen oscl: mHasNotifySubscriptionInfoChangedOccurred==false no callback"); } } } @@ -432,15 +441,61 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(callback.asBinder()); } + + @Override + public void addOnOpportunisticSubscriptionsChangedListener(String callingPackage, + IOnSubscriptionsChangedListener callback) { + int callerUserId = UserHandle.getCallingUserId(); + mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); + if (VDBG) { + log("listen ooscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId() + + " callerUserId=" + callerUserId + " callback=" + callback + + " callback.asBinder=" + callback.asBinder()); + } + + synchronized (mRecords) { + // register + IBinder b = callback.asBinder(); + Record r = add(b); + + if (r == null) { + return; + } + + r.context = mContext; + r.onOpportunisticSubscriptionsChangedListenerCallback = callback; + r.callingPackage = callingPackage; + r.callerUid = Binder.getCallingUid(); + r.callerPid = Binder.getCallingPid(); + r.events = 0; + if (DBG) { + log("listen ooscl: Register r=" + r); + } + // Always notify when registration occurs if there has been a notification. + if (mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) { + try { + if (VDBG) log("listen ooscl: send to r=" + r); + r.onOpportunisticSubscriptionsChangedListenerCallback.onSubscriptionsChanged(); + if (VDBG) log("listen ooscl: sent to r=" + r); + } catch (RemoteException e) { + if (VDBG) log("listen ooscl: remote exception sending to r=" + r + " e=" + e); + remove(r.binder); + } + } else { + log("listen ooscl: hasNotifyOpptSubInfoChangedOccurred==false no callback"); + } + } + } + @Override public void notifySubscriptionInfoChanged() { if (VDBG) log("notifySubscriptionInfoChanged:"); synchronized (mRecords) { - if (!hasNotifySubscriptionInfoChangedOccurred) { + if (!mHasNotifySubscriptionInfoChangedOccurred) { log("notifySubscriptionInfoChanged: first invocation mRecords.size=" + mRecords.size()); } - hasNotifySubscriptionInfoChangedOccurred = true; + mHasNotifySubscriptionInfoChangedOccurred = true; mRemoveList.clear(); for (Record r : mRecords) { if (r.matchOnSubscriptionsChangedListener()) { @@ -459,6 +514,33 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override + public void notifyOpportunisticSubscriptionInfoChanged() { + if (VDBG) log("notifyOpptSubscriptionInfoChanged:"); + synchronized (mRecords) { + if (!mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) { + log("notifyOpptSubscriptionInfoChanged: first invocation mRecords.size=" + + mRecords.size()); + } + mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = true; + mRemoveList.clear(); + for (Record r : mRecords) { + if (r.matchOnOpportunisticSubscriptionsChangedListener()) { + try { + if (VDBG) log("notifyOpptSubChanged: call oosc to r=" + r); + r.onOpportunisticSubscriptionsChangedListenerCallback + .onSubscriptionsChanged(); + if (VDBG) log("notifyOpptSubChanged: done oosc to r=" + r); + } catch (RemoteException ex) { + if (VDBG) log("notifyOpptSubChanged: RemoteException r=" + r); + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } + + @Override public void listen(String pkgForDebug, IPhoneStateListener callback, int events, boolean notifyNow) { listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, pkgForDebug, callback, diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java index aa5a2e091130..7276222bf19e 100644 --- a/services/core/java/com/android/server/am/ActivityDisplay.java +++ b/services/core/java/com/android/server/am/ActivityDisplay.java @@ -39,10 +39,13 @@ import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY; import static com.android.server.am.ActivityDisplayProto.STACKS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityStackSupervisor.FindTaskResult; import static com.android.server.am.ActivityStackSupervisor.TAG_STATES; +import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -121,6 +124,8 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> private DisplayWindowController mWindowContainerController; + private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); + @VisibleForTesting ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) { this(supervisor, supervisor.mDisplayManager.getDisplay(displayId)); @@ -446,6 +451,41 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> } /** + * Find task for putting the Activity in. + */ + void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay, + FindTaskResult result) { + mTmpFindTaskResult.clear(); + for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityStack stack = getChildAt(stackNdx); + if (!r.hasCompatibleActivityType(stack)) { + if (DEBUG_TASKS) { + Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack); + } + continue; + } + + stack.findTaskLocked(r, mTmpFindTaskResult); + // It is possible to have tasks in multiple stacks with the same root affinity, so + // we should keep looking after finding an affinity match to see if there is a + // better match in another stack. Also, task affinity isn't a good enough reason + // to target a display which isn't the source of the intent, so skip any affinity + // matches not on the specified display. + if (mTmpFindTaskResult.mRecord != null) { + if (mTmpFindTaskResult.mIdealMatch) { + result.setTo(mTmpFindTaskResult); + return; + } else if (isPreferredDisplay) { + // Note: since the traversing through the stacks is top down, the floating + // tasks should always have lower priority than any affinity-matching tasks + // in the fullscreen stacks + result.setTo(mTmpFindTaskResult); + } + } + } + } + + /** * Removes stacks in the input windowing modes from the system if they are of activity type * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED */ diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index e6ff0d8a677a..2ee598fee535 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4349,6 +4349,7 @@ public class ActivityManagerService extends IActivityManager.Stub private final void handleAppDiedLocked(ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; + final boolean clearLaunchStartTime = !restarting && app.removed && app.foregroundActivities; boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1, false /*replacingPid*/); if (!kept && !restarting) { @@ -4388,6 +4389,19 @@ public class ActivityManagerService extends IActivityManager.Stub } finally { mWindowManager.continueSurfaceLayout(); } + + // TODO (b/67683350) + // When an app process is removed, activities from the process may be relaunched. In the + // case of forceStopPackageLocked the activities are finished before any window is drawn, + // and the launch time is not cleared. This will be incorrectly used to calculate launch + // time for the next launched activity launched in the same windowing mode. + if (clearLaunchStartTime) { + final LaunchTimeTracker.Entry entry = mStackSupervisor + .getLaunchTimeTracker().getEntry(mStackSupervisor.getWindowingMode()); + if (entry != null) { + entry.mLaunchStartTime = 0; + } + } } private final int getLRURecordIndexForAppLocked(IApplicationThread thread) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 9de687527364..b24c36ad9179 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -16,6 +16,7 @@ package com.android.server.am; +import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM; import static android.app.ActivityTaskManager.RESIZE_MODE_USER; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; @@ -302,7 +303,9 @@ final class ActivityManagerShellCommand extends ShellCommand { mSamplingInterval = 0; mAutoStop = false; mStreaming = false; - mUserId = defUser; + mUserId = mInternal.mUserController.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), defUser, false, ALLOW_FULL_ONLY, + "ActivityManagerShellCommand", null); mDisplayId = INVALID_DISPLAY; mWindowingMode = WINDOWING_MODE_UNDEFINED; mActivityType = ACTIVITY_TYPE_UNDEFINED; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 355d890f1bd1..bc99827be47e 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1176,8 +1176,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai //dump(); if (DEBUG_TASKS) Slog.d(TAG_TASKS, "For Intent " + intent + " bringing to top: " + r.intent); - result.r = r; - result.matchedByRootAffinity = false; + result.mRecord = r; + result.mIdealMatch = true; break; } else if (affinityIntent != null && affinityIntent.getComponent() != null && affinityIntent.getComponent().compareTo(cls) == 0 && @@ -1186,18 +1186,18 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai //dump(); if (DEBUG_TASKS) Slog.d(TAG_TASKS, "For Intent " + intent + " bringing to top: " + r.intent); - result.r = r; - result.matchedByRootAffinity = false; + result.mRecord = r; + result.mIdealMatch = true; break; } else if (!isDocument && !taskIsDocument - && result.r == null && task.rootAffinity != null) { + && result.mRecord == null && task.rootAffinity != null) { if (task.rootAffinity.equals(target.taskAffinity)) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!"); // It is possible for multiple tasks to have the same root affinity especially // if they are in separate stacks. We save off this candidate, but keep looking // to see if there is a better candidate. - result.r = r; - result.matchedByRootAffinity = true; + result.mRecord = r; + result.mIdealMatch = false; } } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 1ffdc6738c1d..af2d3b0d47fd 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -533,9 +533,20 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } static class FindTaskResult { - ActivityRecord r; - boolean matchedByRootAffinity; + ActivityRecord mRecord; + boolean mIdealMatch; + + void clear() { + mRecord = null; + mIdealMatch = false; + } + + void setTo(FindTaskResult result) { + mRecord = result.mRecord; + mIdealMatch = result.mIdealMatch; + } } + private final FindTaskResult mTmpFindTaskResult = new FindTaskResult(); /** @@ -3459,44 +3470,33 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return true; } - ActivityRecord findTaskLocked(ActivityRecord r, int displayId) { - mTmpFindTaskResult.r = null; - mTmpFindTaskResult.matchedByRootAffinity = false; - ActivityRecord affinityMatch = null; + ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) { if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r); + mTmpFindTaskResult.clear(); + + // Looking up task on preferred display first + final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId); + if (preferredDisplay != null) { + preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; + } + } + for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ActivityDisplay display = mActivityDisplays.get(displayNdx); - for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityStack stack = display.getChildAt(stackNdx); - if (!r.hasCompatibleActivityType(stack)) { - if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " - + stack); - continue; - } - stack.findTaskLocked(r, mTmpFindTaskResult); - // It is possible to have tasks in multiple stacks with the same root affinity, so - // we should keep looking after finding an affinity match to see if there is a - // better match in another stack. Also, task affinity isn't a good enough reason - // to target a display which isn't the source of the intent, so skip any affinity - // matches not on the specified display. - if (mTmpFindTaskResult.r != null) { - if (!mTmpFindTaskResult.matchedByRootAffinity) { - return mTmpFindTaskResult.r; - } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) { - // Note: since the traversing through the stacks is top down, the floating - // tasks should always have lower priority than any affinity-matching tasks - // in the fullscreen stacks - affinityMatch = mTmpFindTaskResult.r; - } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) { - Slog.d(TAG_TASKS, "Skipping match on different display " - + mTmpFindTaskResult.r.getDisplayId() + " " + displayId); - } - } + if (display.mDisplayId == preferredDisplayId) { + continue; + } + + display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult); + if (mTmpFindTaskResult.mIdealMatch) { + return mTmpFindTaskResult.mRecord; } } - if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found"); - return affinityMatch; + if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found"); + return mTmpFindTaskResult.mRecord; } ActivityRecord findActivityLocked(Intent intent, ActivityInfo info, diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java index 11f8bb1e2883..7eadcb307028 100644 --- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java @@ -27,17 +27,7 @@ import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STOP_APP_SWITCHES; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.content.pm.PackageManager.FEATURE_PC; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; -import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; -import static android.provider.Settings.System.FONT_SCALE; -import static com.android.server.am.ActivityManagerService.dumpStackTraces; -import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG; -import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; -import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; +import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; import static android.app.AppOpsManager.OP_NONE; @@ -51,15 +41,19 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; +import static android.content.pm.PackageManager.FEATURE_PC; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION; import static android.os.Build.VERSION_CODES.N; import static android.os.Process.SYSTEM_UID; +import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; +import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; +import static android.provider.Settings.System.FONT_SCALE; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -68,6 +62,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; + import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS; @@ -86,13 +81,13 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH; import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static com.android.server.am.ActivityManagerService.ANIMATE; import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG; import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG; import static com.android.server.am.ActivityManagerService.checkComponentPermission; +import static com.android.server.am.ActivityManagerService.dumpStackTraces; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY; @@ -100,10 +95,16 @@ import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS import static com.android.server.am.ActivityStackSupervisor.ON_TOP; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS; +import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG; +import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG; import static com.android.server.am.TaskRecord.INVALID_TASK_ID; import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT; import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; @@ -118,26 +119,8 @@ import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManagerInternal; -import android.database.ContentObserver; -import android.os.IUserManager; -import android.os.PowerManager; -import android.os.ServiceManager; -import android.os.Trace; -import android.os.UserManager; -import android.os.WorkSource; -import android.view.WindowManager; -import com.android.internal.R; -import com.android.internal.app.IAppOpsService; -import com.android.server.AppOpsService; -import com.android.server.SystemServiceManager; -import com.android.server.pm.UserManagerService; -import com.android.server.uri.UriGrantsManagerInternal; -import com.android.server.wm.ActivityTaskManagerInternal; import android.app.AppGlobals; +import android.app.Dialog; import android.app.IActivityController; import android.app.IActivityTaskManager; import android.app.IApplicationThread; @@ -157,15 +140,19 @@ import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.IIntentSender; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Point; import android.graphics.Rect; @@ -177,16 +164,22 @@ import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; +import android.os.IUserManager; import android.os.LocaleList; import android.os.Looper; import android.os.Message; import android.os.PersistableBundle; +import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; +import android.os.Trace; import android.os.UpdateLock; import android.os.UserHandle; +import android.os.UserManager; +import android.os.WorkSource; import android.provider.Settings; import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionManagerInternal; @@ -197,7 +190,6 @@ import android.util.ArrayMap; import android.util.EventLog; import android.util.Log; import android.util.Slog; - import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StatsLog; @@ -206,22 +198,30 @@ import android.util.proto.ProtoOutputStream; import android.view.IRecentsAnimationRunner; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationDefinition; +import android.view.WindowManager; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.AssistUtils; +import com.android.internal.app.IAppOpsService; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; import com.android.internal.logging.MetricsLogger; -import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.os.logging.MetricsLoggerWrapper; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.KeyguardDismissCallback; import com.android.internal.util.Preconditions; +import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.SystemServiceManager; import com.android.server.Watchdog; +import com.android.server.pm.UserManagerService; +import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.vr.VrManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.PinnedStackWindowController; import com.android.server.wm.WindowManagerService; diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index bc2331639005..0a7e127dde22 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -414,7 +414,11 @@ public final class BroadcastQueue { if (state == BroadcastRecord.IDLE) { Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE"); } - r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime; + // If we're abandoning this broadcast before any receivers were actually spun up, + // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply. + if (r.nextReceiver > 0) { + r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime; + } r.receiver = null; r.intent.setComponent(null); if (r.curApp != null && r.curApp.curReceivers.contains(r)) { diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 4ca96a1904db..b817669ce70c 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -49,6 +49,20 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] + }, + { + "name": "WmTests", + "options": [ + { + "include-filter": "com.android.server.am." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ], "postsubmit": [ @@ -65,6 +79,14 @@ "include-filter": "com.android.server.am." } ] + }, + { + "name": "WmTests", + "options": [ + { + "include-filter": "com.android.server.am." + } + ] } ] } 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 f211e1716d13..8afac97f647f 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -574,7 +574,8 @@ public class FaceService extends BiometricService { for (int i = 0; i < cryptoToken.length; i++) { token.add(cryptoToken[i]); } - return daemon.enroll(token, timeout); + // TODO: plumb requireAttention down from framework + return daemon.enroll(token, timeout, true /* requireAttention */); } }; @@ -757,7 +758,7 @@ public class FaceService extends BiometricService { return 0; } try { - return daemon.preEnroll().value; + return daemon.generateChallenge().value; } catch (RemoteException e) { Slog.e(TAG, "startPreEnroll failed", e); } @@ -771,7 +772,7 @@ public class FaceService extends BiometricService { return 0; } try { - return daemon.postEnroll(); + return daemon.revokeChallenge(); } catch (RemoteException e) { Slog.e(TAG, "startPostEnroll failed", e); } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 260633ac4feb..389782a91d06 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -2413,6 +2413,8 @@ public class JobSchedulerService extends com.android.server.SystemService BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService (BatteryStatsInternal.class); mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast); + StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null, + counter.numDeferred(), sinceLast); } } } diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index b922e40a5d38..6f9d8033cd88 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -43,6 +43,7 @@ import android.os.FileUtils; import android.os.IBinder; import android.os.IStatsCompanionService; import android.os.IStatsManager; +import android.os.IStoraged; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; @@ -54,6 +55,7 @@ import android.os.SynchronousResultReceiver; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.os.storage.StorageManager; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -76,9 +78,19 @@ import com.android.internal.util.DumpUtils; import com.android.server.BinderCallsStatsService; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.storage.DiskStatsFileLogger; +import com.android.server.storage.DiskStatsLoggingService; + +import libcore.io.IoUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import java.io.File; import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -861,14 +873,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pulledData.add(e); } - private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) { - StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 3); - e.writeLong(mStatFsData.getAvailableBytes()); - e.writeLong(mStatFsSystem.getAvailableBytes()); - e.writeLong(mStatFsTemp.getAvailableBytes()); - pulledData.add(e); - } - private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) { StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1); e.writeLong(SystemClock.uptimeMillis()); @@ -962,6 +966,183 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) { + // Run a quick-and-dirty performance test: write 512 bytes + byte[] junk = new byte[512]; + for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes + + File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp"); + FileOutputStream fos = null; + IOException error = null; + + long before = SystemClock.elapsedRealtime(); + try { + fos = new FileOutputStream(tmp); + fos.write(junk); + } catch (IOException e) { + error = e; + } finally { + try { + if (fos != null) fos.close(); + } catch (IOException e) { + // Do nothing. + } + } + + long latency = SystemClock.elapsedRealtime() - before; + if (tmp.exists()) tmp.delete(); + + if (error != null) { + Slog.e(TAG, "Error performing diskstats latency test"); + latency = -1; + } + // File based encryption. + boolean fileBased = StorageManager.isFileEncryptedNativeOnly(); + + //Recent disk write speed. Binder call to storaged. + int writeSpeed = -1; + try { + IBinder binder = ServiceManager.getService("storaged"); + if (binder == null) { + Slog.e(TAG, "storaged not found"); + } + IStoraged storaged = IStoraged.Stub.asInterface(binder); + writeSpeed = storaged.getRecentPerf(); + } catch (RemoteException e) { + Slog.e(TAG, "storaged not found"); + } + + // Add info pulledData. + long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeLong(latency); + e.writeBoolean(fileBased); + e.writeInt(writeSpeed); + pulledData.add(e); + } + + private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) { + long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath()); + StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath()); + StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath()); + + StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA); + e.writeLong(statFsData.getAvailableBytes()); + e.writeLong(statFsData.getTotalBytes()); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE); + e.writeLong(statFsCache.getAvailableBytes()); + e.writeLong(statFsCache.getTotalBytes()); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM); + e.writeLong(statFsSystem.getAvailableBytes()); + e.writeLong(statFsSystem.getTotalBytes()); + pulledData.add(e); + } + + private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) { + long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + try { + String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); + JSONObject json = new JSONObject(jsonStr); + long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L); + JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY); + JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY); + JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY); + JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY); + // Sanity check: Ensure all 4 lists have the same length. + int length = pkg_names.length(); + if (app_sizes.length() != length || app_data_sizes.length() != length + || app_cache_sizes.length() != length) { + Slog.e(TAG, "formatting error in diskstats cache file!"); + return; + } + for (int i = 0; i < length; i++) { + StatsLogEventWrapper e = + new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */); + e.writeString(pkg_names.getString(i)); + e.writeLong(app_sizes.optLong(i, -1L)); + e.writeLong(app_data_sizes.optLong(i, -1L)); + e.writeLong(app_cache_sizes.optLong(i, -1L)); + e.writeLong(cache_time); + pulledData.add(e); + } + } catch (IOException | JSONException e) { + Slog.e(TAG, "exception reading diskstats cache file", e); + } + } + + private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) { + long elapsedNanos = SystemClock.elapsedRealtimeNanos(); + try { + String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH); + JSONObject json = new JSONObject(jsonStr); + long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L); + + StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE); + e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE); + e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE); + e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS); + e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS); + e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO); + e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS); + e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM); + e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + + e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */); + e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER); + e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L)); + e.writeLong(cacheTime); + pulledData.add(e); + } catch (IOException | JSONException e) { + Slog.e(TAG, "exception reading diskstats cache file", e); + } + } + /** * Pulls various data. */ @@ -1036,10 +1217,6 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullSystemElapsedRealtime(tagId, ret); break; } - case StatsLog.DISK_SPACE: { - pullDiskSpace(tagId, ret); - break; - } case StatsLog.PROCESS_MEMORY_STATE: { pullProcessMemoryState(tagId, ret); break; @@ -1056,6 +1233,22 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullLooperStats(tagId, ret); break; } + case StatsLog.DISK_STATS: { + pullDiskStats(tagId, ret); + break; + } + case StatsLog.DIRECTORY_USAGE: { + pullDirectoryUsage(tagId, ret); + break; + } + case StatsLog.APP_SIZE: { + pullAppSize(tagId, ret); + break; + } + case StatsLog.CATEGORY_SIZE: { + pullCategorySize(tagId, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index b7759182fb01..6da9f104a212 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -32,29 +32,11 @@ import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; - +import static android.view.WindowManager.TRANSIT_UNSET; import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN; + import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; -import static android.view.WindowManager.TRANSIT_UNSET; - -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; -import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; -import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; -import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN; import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED; import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN; @@ -78,6 +60,23 @@ import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED; import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW; import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL; import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN; +import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; +import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; +import static com.android.server.wm.WindowManagerService.logWithStack; import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; import android.annotation.CallSuper; @@ -1845,8 +1844,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree surfaceInsets = win.getAttrs().surfaceInsets; // XXX(b/72757033): These are insets relative to the window frame, but we're really // interested in the insets relative to the frame we chose in the if-blocks above. - insets.set(win.mContentInsets); - stableInsets.set(win.mStableInsets); + win.getContentInsets(insets); + win.getStableInsets(stableInsets); } if (mLaunchTaskBehind) { @@ -2099,7 +2098,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree WindowState win = findMainWindow(); Rect appRect = win != null ? win.getContentFrameLw() : new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight); - Rect insets = win != null ? win.mContentInsets : null; + final Rect insets = win != null ? win.getContentInsets() : null; final Configuration displayConfig = mDisplayContent.getConfiguration(); return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked( appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode, diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index a6930717ff91..731ebb8a6e86 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -26,9 +26,9 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.graphics.Rect; +import android.os.Debug; import android.os.Handler; import android.os.IBinder; -import android.os.Debug; import android.util.ArrayMap; import android.util.Slog; import android.view.Choreographer; @@ -36,8 +36,8 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import com.android.internal.annotations.VisibleForTesting; - import com.android.internal.graphics.SfVsyncFrameCallbackProvider; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -178,7 +178,7 @@ public class BoundsAnimationController { BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, @SchedulePipModeChangedState int prevShedulePipModeChangedState, - boolean moveFromFullscreen, boolean moveToFullscreen) { + boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) { super(); mTarget = target; mFrom.set(from); @@ -198,8 +198,8 @@ public class BoundsAnimationController { mFrozenTaskWidth = mTo.width(); mFrozenTaskHeight = mTo.height(); } else { - mFrozenTaskWidth = mFrom.width(); - mFrozenTaskHeight = mFrom.height(); + mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width(); + mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height(); } } @@ -425,6 +425,7 @@ public class BoundsAnimationController { + " schedulePipModeChangedState=" + schedulePipModeChangedState + " replacing=" + replacing); + Rect frozenTask = new Rect(); if (replacing) { if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen) && (!moveFromFullscreen || existing.mMoveFromFullscreen)) { @@ -467,12 +468,17 @@ public class BoundsAnimationController { moveFromFullscreen = existing.mMoveFromFullscreen; } + // We are in the middle of an existing animation, so that this new animation may + // start from an interpolated bounds. We should keep using the existing frozen task + // width/height for consistent configurations. + frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight); + // Since we are replacing, we skip both animation start and end callbacks existing.cancel(); } final BoundsAnimator animator = new BoundsAnimator(target, from, to, schedulePipModeChangedState, prevSchedulePipModeChangedState, - moveFromFullscreen, moveToFullscreen); + moveFromFullscreen, moveToFullscreen, frozenTask); mRunningAnimations.put(target, animator); animator.setFloatValues(0f, 1f); animator.setDuration((animationDuration != -1 ? animationDuration diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c57d659b871f..d3e534ce4522 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3008,7 +3008,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) { w.mWinAnimator.mDrawState = DRAW_PENDING; // Force add to mResizingWindows. - w.mLastContentInsets.set(-1, -1, -1, -1); + w.resetLastContentInsets(); mService.mWaitingForDrawn.add(w); } }, true /* traverseTopToBottom */); diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 4dbd858ec45b..1eae56745a75 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -373,7 +373,7 @@ public class RecentsAnimationController implements DeathRecipient { : null; final Rect contentInsets; if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) { - contentInsets = mTargetAppToken.findMainWindow().mContentInsets; + contentInsets = mTargetAppToken.findMainWindow().getContentInsets(); } else { // If the window for the activity had not yet been created, use the display insets. mService.getStableInsets(mDisplayId, mTmpRect); @@ -583,7 +583,8 @@ public class RecentsAnimationController implements DeathRecipient { if (mainWindow == null) { return null; } - final Rect insets = new Rect(mainWindow.mContentInsets); + final Rect insets = new Rect(); + mainWindow.getContentInsets(insets); InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets()); mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash, !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 67ef47103103..00422e3497be 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -305,7 +305,8 @@ class RemoteAnimationController implements DeathRecipient { || mCapturedLeash == null) { return null; } - final Rect insets = new Rect(mainWindow.mContentInsets); + final Rect insets = new Rect(); + mainWindow.getContentInsets(insets); InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets()); mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(), mCapturedLeash, !mAppWindowToken.fillsParent(), diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING index e885afa8031d..c99329a7508c 100644 --- a/services/core/java/com/android/server/wm/TEST_MAPPING +++ b/services/core/java/com/android/server/wm/TEST_MAPPING @@ -24,6 +24,20 @@ "exclude-annotation": "android.support.test.filters.FlakyTest" } ] + }, + { + "name": "WmTests", + "options": [ + { + "include-filter": "com.android.server.wm." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "android.support.test.filters.FlakyTest" + } + ] } ], "postsubmit": [ @@ -37,6 +51,14 @@ "include-filter": "com.android.server.wm." } ] + }, + { + "name": "WmTests", + "options": [ + { + "include-filter": "com.android.server.wm." + } + ] } ] } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 6c8572a864c6..b7507a42485c 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -303,7 +303,7 @@ class TaskSnapshotController { private Rect getInsets(WindowState state) { // XXX(b/72757033): These are insets relative to the window frame, but we're really // interested in the insets relative to the task bounds. - final Rect insets = minRect(state.mContentInsets, state.mStableInsets); + final Rect insets = minRect(state.getContentInsets(), state.getStableInsets()); InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets()); return insets; } @@ -373,7 +373,7 @@ class TaskSnapshotController { node.setClipToBounds(false); final DisplayListCanvas c = node.start(width, height); c.drawColor(color); - decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets); + decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets()); decorPainter.drawDecors(c, null /* statusBarExcludeFrame */); node.end(c); final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height); @@ -383,7 +383,7 @@ class TaskSnapshotController { // Note, the app theme snapshot is never translucent because we enforce a non-translucent // color above return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(), - topChild.getConfiguration().orientation, mainWindow.mStableInsets, + topChild.getConfiguration().orientation, mainWindow.getStableInsets(), ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */, false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task), false); diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index 228bfade25d5..9381fc61821e 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -18,20 +18,27 @@ package com.android.server.wm; import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME; import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME; +import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS; import static com.android.server.wm.WindowFramesProto.CUTOUT; import static com.android.server.wm.WindowFramesProto.DECOR_FRAME; import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME; import static com.android.server.wm.WindowFramesProto.FRAME; +import static com.android.server.wm.WindowFramesProto.OUTSETS; import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME; import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME; +import static com.android.server.wm.WindowFramesProto.OVERSCAN_INSETS; import static com.android.server.wm.WindowFramesProto.PARENT_FRAME; +import static com.android.server.wm.WindowFramesProto.STABLE_INSETS; import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME; +import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS; import android.annotation.NonNull; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; +import android.view.WindowManager; +import com.android.server.wm.utils.InsetUtils; import com.android.server.wm.utils.WmDisplayCutout; import java.io.PrintWriter; @@ -60,7 +67,7 @@ public class WindowFrames { * * TODO(b/111611553): The name is unclear and most likely should be swapped with * {@link #mParentFrame} - */ + */ public final Rect mDisplayFrame = new Rect(); /** @@ -118,6 +125,12 @@ public class WindowFrames { */ final Rect mLastFrame = new Rect(); + private boolean mFrameSizeChanged = false; + + // Frame that is scaled to the application's coordinate space when in + // screen size compatibility mode. + final Rect mCompatFrame = new Rect(); + /** * Whether the parent frame would have been different if there was no display cutout. */ @@ -131,7 +144,52 @@ public class WindowFrames { /** * The last cutout that has been reported to the client. */ - WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT; + private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT; + + private boolean mDisplayCutoutChanged; + + /** + * Insets that determine the area covered by the display overscan region. These are in the + * application's coordinate space (without compatibility scale applied). + */ + final Rect mOverscanInsets = new Rect(); + final Rect mLastOverscanInsets = new Rect(); + private boolean mOverscanInsetsChanged; + + /** + * Insets that determine the area covered by the stable system windows. These are in the + * application's coordinate space (without compatibility scale applied). + */ + final Rect mStableInsets = new Rect(); + final Rect mLastStableInsets = new Rect(); + private boolean mStableInsetsChanged; + + /** + * Outsets determine the area outside of the surface where we want to pretend that it's possible + * to draw anyway. + */ + final Rect mOutsets = new Rect(); + final Rect mLastOutsets = new Rect(); + private boolean mOutsetsChanged = false; + + /** + * Insets that determine the actually visible area. These are in the application's + * coordinate space (without compatibility scale applied). + */ + final Rect mVisibleInsets = new Rect(); + final Rect mLastVisibleInsets = new Rect(); + private boolean mVisibleInsetsChanged; + + /** + * Insets that are covered by system windows (such as the status bar) and + * transient docking windows (such as the IME). These are in the application's + * coordinate space (without compatibility scale applied). + */ + final Rect mContentInsets = new Rect(); + final Rect mLastContentInsets = new Rect(); + private boolean mContentInsetsChanged; + + private final Rect mTmpRect = new Rect(); public WindowFrames() { } @@ -171,15 +229,141 @@ public class WindowFrames { /** * @return true if the width or height has changed since last reported to the client. */ - boolean didFrameSizeChange() { + private boolean didFrameSizeChange() { return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height()); } /** - * @return true if the display cutout has changed since last reported to the client. + * Calculates the outsets for this windowFrame. The outsets are calculated by the area between + * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then + * {@link #mOutsets} is set to empty. + * + * @param hasOutsets Whether this frame has outsets. + */ + void calculateOutsets(boolean hasOutsets) { + if (hasOutsets) { + InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets); + } else { + mOutsets.setEmpty(); + } + } + + /** + * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER} + * + * @param cutoutInsets The insets for the cutout. */ - boolean didDisplayCutoutChange() { - return !mLastDisplayCutout.equals(mDisplayCutout); + void calculateDockedDividerInsets(Rect cutoutInsets) { + // For the docked divider, we calculate the stable insets like a full-screen window + // so it can use it to calculate the snap positions. + mTmpRect.set(mDisplayFrame); + mTmpRect.inset(cutoutInsets); + mTmpRect.intersectUnchecked(mStableFrame); + InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets); + + // The divider doesn't care about insets in any case, so set it to empty so we don't + // trigger a relayout when moving it. + mContentInsets.setEmpty(); + mVisibleInsets.setEmpty(); + mDisplayCutout = WmDisplayCutout.NO_CUTOUT; + } + + /** + * Calculate the insets for a window. + * + * @param windowsAreFloating Whether the window is in a floating task such as pinned or + * freeform + * @param inFullscreenContainer Whether the window is in a container that takes up the screen's + * entire space + * @param windowBounds The bounds for the window + */ + void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer, + Rect windowBounds) { + // Override right and/or bottom insets in case if the frame doesn't fit the screen in + // non-fullscreen mode. + boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer + && mFrame.right > windowBounds.right; + boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer + && mFrame.bottom > windowBounds.bottom; + + mTmpRect.set(mFrame.left, mFrame.top, overrideRightInset ? mTmpRect.right : mFrame.right, + overrideBottomInset ? mTmpRect.bottom : mFrame.bottom); + + InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets); + InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets); + InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets); + } + + /** + * Scales all the insets by a specific amount. + * + * @param scale The amount to scale the insets by. + */ + void scaleInsets(float scale) { + mOverscanInsets.scale(scale); + mContentInsets.scale(scale); + mVisibleInsets.scale(scale); + mStableInsets.scale(scale); + mOutsets.scale(scale); + } + + void offsetFrames(int layoutXDiff, int layoutYDiff) { + mFrame.offset(layoutXDiff, layoutYDiff); + mContentFrame.offset(layoutXDiff, layoutYDiff); + mVisibleFrame.offset(layoutXDiff, layoutYDiff); + mStableFrame.offset(layoutXDiff, layoutYDiff); + } + + /** + * Updates info about whether the size of the window has changed since last reported. + * + * @return true if info about size has changed since last reported. + */ + boolean setReportResizeHints() { + mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets); + mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets); + mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets); + mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets); + mOutsetsChanged |= !mLastOutsets.equals(mOutsets); + mFrameSizeChanged |= didFrameSizeChange(); + mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout); + return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged + || mStableInsetsChanged || mOutsetsChanged || mFrameSizeChanged + || mDisplayCutoutChanged; + } + + /** + * Resets the insets changed flags so they're all set to false again. This should be called + * after the insets are reported to client. + */ + void resetInsetsChanged() { + mOverscanInsetsChanged = false; + mContentInsetsChanged = false; + mVisibleInsetsChanged = false; + mStableInsetsChanged = false; + mOutsetsChanged = false; + mFrameSizeChanged = false; + mDisplayCutoutChanged = false; + } + + /** + * Copy over inset values as the last insets that were sent to the client. + */ + void updateLastInsetValues() { + mLastOverscanInsets.set(mOverscanInsets); + mLastContentInsets.set(mContentInsets); + mLastVisibleInsets.set(mVisibleInsets); + mLastStableInsets.set(mStableInsets); + mLastOutsets.set(mOutsets); + mLastDisplayCutout = mDisplayCutout; + } + + /** + * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update + * the client. + */ + void resetLastContentInsets() { + mLastContentInsets.set(-1, -1, -1, -1); } public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) { @@ -194,6 +378,12 @@ public class WindowFrames { mContainingFrame.writeToProto(proto, CONTAINING_FRAME); mFrame.writeToProto(proto, FRAME); mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT); + mContentInsets.writeToProto(proto, CONTENT_INSETS); + mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS); + mVisibleInsets.writeToProto(proto, VISIBLE_INSETS); + mStableInsets.writeToProto(proto, STABLE_INSETS); + mOutsets.writeToProto(proto, OUTSETS); + proto.end(token); } @@ -211,5 +401,34 @@ public class WindowFrames { + " last=" + mLastFrame.toShortString(sTmpSB)); pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout() + " last=" + mLastDisplayCutout.getDisplayCutout()); + pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB) + + " content=" + mContentInsets.toShortString(sTmpSB) + + " visible=" + mVisibleInsets.toShortString(sTmpSB) + + " stable=" + mStableInsets.toShortString(sTmpSB) + + " outsets=" + mOutsets.toShortString(sTmpSB)); + pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB) + + " content=" + mLastContentInsets.toShortString(sTmpSB) + + " visible=" + mLastVisibleInsets.toShortString(sTmpSB) + + " stable=" + mLastStableInsets.toShortString(sTmpSB) + + " outset=" + mLastOutsets.toShortString(sTmpSB)); + } + + String getInsetsInfo() { + return "ci=" + mContentInsets.toShortString() + + " vi=" + mVisibleInsets.toShortString() + + " si=" + mStableInsets.toShortString() + + " of=" + mOutsets.toShortString(); + } + + String getInsetsChangedInfo() { + return "contentInsetsChanged=" + mContentInsetsChanged + + " " + mContentInsets.toShortString() + + " visibleInsetsChanged=" + mVisibleInsetsChanged + + " " + mVisibleInsets.toShortString() + + " stableInsetsChanged=" + mStableInsetsChanged + + " " + mStableInsets.toShortString() + + " outsetsChanged=" + mOutsetsChanged + + " " + mOutsets.toShortString() + + " displayCutoutChanged=" + mDisplayCutoutChanged; } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5279b4301b48..679e0d870d74 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2168,14 +2168,10 @@ public class WindowManagerService extends IWindowManager.Stub // The last inset values represent the last client state. win.updateLastInsetValues(); - outFrame.set(win.mCompatFrame); - outOverscanInsets.set(win.mOverscanInsets); - outContentInsets.set(win.mContentInsets); - win.mLastRelayoutContentInsets.set(win.mContentInsets); - outVisibleInsets.set(win.mVisibleInsets); - outStableInsets.set(win.mStableInsets); + win.getCompatFrame(outFrame); + win.getInsetsForRelayout(outOverscanInsets, outContentInsets, outVisibleInsets, + outStableInsets, outOutsets); outCutout.set(win.getWmDisplayCutout().getDisplayCutout()); - outOutsets.set(win.mOutsets); outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw())); if (localLOGV) Slog.v( TAG_WM, "Relayout given client " + client.asBinder() diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 466e298974d0..637c0eabdac4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -120,7 +120,6 @@ import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT; import static com.android.server.wm.WindowStateProto.ANIMATOR; import static com.android.server.wm.WindowStateProto.ATTRIBUTES; import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS; -import static com.android.server.wm.WindowStateProto.CONTENT_INSETS; import static com.android.server.wm.WindowStateProto.DESTROYING; import static com.android.server.wm.WindowStateProto.DISPLAY_ID; import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME; @@ -131,20 +130,16 @@ import static com.android.server.wm.WindowStateProto.IDENTIFIER; import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN; import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY; import static com.android.server.wm.WindowStateProto.IS_VISIBLE; -import static com.android.server.wm.WindowStateProto.OUTSETS; -import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS; import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION; import static com.android.server.wm.WindowStateProto.REMOVED; import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT; import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT; import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH; -import static com.android.server.wm.WindowStateProto.STABLE_INSETS; import static com.android.server.wm.WindowStateProto.STACK_ID; import static com.android.server.wm.WindowStateProto.SURFACE_INSETS; import static com.android.server.wm.WindowStateProto.SURFACE_POSITION; import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY; import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; -import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; @@ -200,6 +195,7 @@ import com.android.internal.util.ToBooleanFunction; import com.android.server.input.InputWindowHandle; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; +import com.android.server.wm.utils.InsetUtils; import com.android.server.wm.utils.WmDisplayCutout; import java.io.PrintWriter; @@ -309,22 +305,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration(); /** - * Insets that determine the actually visible area. These are in the application's - * coordinate space (without compatibility scale applied). - */ - final Rect mVisibleInsets = new Rect(); - private final Rect mLastVisibleInsets = new Rect(); - private boolean mVisibleInsetsChanged; - - /** - * Insets that are covered by system windows (such as the status bar) and - * transient docking windows (such as the IME). These are in the application's - * coordinate space (without compatibility scale applied). - */ - final Rect mContentInsets = new Rect(); - final Rect mLastContentInsets = new Rect(); - - /** * The last content insets returned to the client in relayout. We use * these in the bounds animation to ensure we only observe inset changes * at the same time that a client resizes it's surface so that we may use @@ -333,34 +313,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ final Rect mLastRelayoutContentInsets = new Rect(); - private boolean mContentInsetsChanged; - - /** - * Insets that determine the area covered by the display overscan region. These are in the - * application's coordinate space (without compatibility scale applied). - */ - final Rect mOverscanInsets = new Rect(); - private final Rect mLastOverscanInsets = new Rect(); - private boolean mOverscanInsetsChanged; - - /** - * Insets that determine the area covered by the stable system windows. These are in the - * application's coordinate space (without compatibility scale applied). - */ - final Rect mStableInsets = new Rect(); - private final Rect mLastStableInsets = new Rect(); - private boolean mStableInsetsChanged; - - /** - * Outsets determine the area outside of the surface where we want to pretend that it's possible - * to draw anyway. - */ - final Rect mOutsets = new Rect(); - private final Rect mLastOutsets = new Rect(); - private boolean mOutsetsChanged = false; - - private boolean mDisplayCutoutChanged; - /** * Set to true if we are waiting for this window to receive its * given internal insets before laying out other windows based on it. @@ -399,11 +351,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP float mLastHScale=1, mLastVScale=1; final Matrix mTmpMatrix = new Matrix(); - private boolean mFrameSizeChanged = false; - // Frame that is scaled to the application's coordinate space when in - // screen size compatibility mode. - final Rect mCompatFrame = new Rect(); - private final WindowFrames mWindowFrames = new WindowFrames(); /** @@ -988,17 +935,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame); // Calculate the outsets before the content frame gets shrinked to the window frame. - if (hasOutsets) { - mOutsets.set( - Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0), - Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0), - Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right, - 0), - Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom, - 0)); - } else { - mOutsets.set(0, 0, 0, 0); - } + mWindowFrames.calculateOutsets(hasOutsets); // Make sure the content and visible frames are inside of the // final window frame. @@ -1055,90 +992,35 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (inFullscreenContainer && !windowsAreFloating) { // Windows that are not fullscreen can be positioned outside of the display frame, // but that is not a reason to provide them with overscan insets. - mOverscanInsets.set( - Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0), - Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0), - Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0), - Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom, - 0)); + InsetUtils.insetsBetweenFrames(layoutContainingFrame, mWindowFrames.mOverscanFrame, + mWindowFrames.mOverscanInsets); } if (mAttrs.type == TYPE_DOCK_DIVIDER) { - // For the docked divider, we calculate the stable insets like a full-screen window - // so it can use it to calculate the snap positions. final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo( mWindowFrames.mDisplayFrame); - mTmpRect.set(mWindowFrames.mDisplayFrame); - mTmpRect.inset(c.getDisplayCutout().getSafeInsets()); - mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame); - - mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0), - Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0), - Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0), - Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0)); - - // The divider doesn't care about insets in any case, so set it to empty so we don't - // trigger a relayout when moving it. - mContentInsets.setEmpty(); - mVisibleInsets.setEmpty(); - windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT); + mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets()); } else { getDisplayContent().getBounds(mTmpRect); - // Override right and/or bottom insets in case if the frame doesn't fit the screen in - // non-fullscreen mode. - boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer - && mWindowFrames.mFrame.right > mTmpRect.right; - boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer - && mWindowFrames.mFrame.bottom > mTmpRect.bottom; - mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left, - mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top, - overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right - : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right, - overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom - : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom); - - mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left, - mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top, - overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right - : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right, - overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom - : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom); - - mStableInsets.set( - Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0), - Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0), - overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right, - 0) : Math.max( - mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0), - overrideBottomInset ? Math.max( - mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max( - mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0)); + mWindowFrames.calculateInsets(windowsAreFloating, inFullscreenContainer, mTmpRect); } mWindowFrames.setDisplayCutout( windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame)); // Offset the actual frame by the amount layout frame is off. - mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff); - mCompatFrame.offset(-layoutXDiff, -layoutYDiff); - mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff); - mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff); - mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff); + mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff); - mCompatFrame.set(mWindowFrames.mFrame); + mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); if (mEnforceSizeCompat) { // If there is a size compatibility scale being applied to the // window, we need to apply this to its insets so that they are // reported to the app in its coordinate space. - mOverscanInsets.scale(mInvGlobalScale); - mContentInsets.scale(mInvGlobalScale); - mVisibleInsets.scale(mInvGlobalScale); - mStableInsets.scale(mInvGlobalScale); - mOutsets.scale(mInvGlobalScale); + mWindowFrames.scaleInsets(mInvGlobalScale); // Also the scaled frame that we report to the app needs to be // adjusted to be in its coordinate space. - mCompatFrame.scale(mInvGlobalScale); + mWindowFrames.mCompatFrame.scale(mInvGlobalScale); } if (mIsWallpaper && (fw != mWindowFrames.mFrame.width() @@ -1156,10 +1038,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + mRequestedWidth + ", mRequestedheight=" + mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph + "): frame=" + mWindowFrames.mFrame.toShortString() - + " ci=" + mContentInsets.toShortString() - + " vi=" + mVisibleInsets.toShortString() - + " si=" + mStableInsets.toShortString() - + " of=" + mOutsets.toShortString()); + + " " + mWindowFrames.getInsetsInfo()); } // TODO: Look into whether this override is still necessary. @@ -1219,6 +1098,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mWindowFrames.mDisplayCutout; } + void getCompatFrame(Rect outFrame) { + outFrame.set(mWindowFrames.mCompatFrame); + } + + void getCompatFrameSize(Rect outFrame) { + outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height()); + } + @Override public boolean getGivenInsetsPendingLw() { return mGivenInsetsPending; @@ -1270,15 +1157,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean setReportResizeHints() { - mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets); - mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets); - mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets); - mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets); - mOutsetsChanged |= !mLastOutsets.equals(mOutsets); - mFrameSizeChanged |= mWindowFrames.didFrameSizeChange(); - mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange(); - return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged - || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged; + return mWindowFrames.setReportResizeHints(); } /** @@ -1301,7 +1180,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } - setReportResizeHints(); + boolean didFrameInsetsChange = setReportResizeHints(); boolean configChanged = isConfigChanged(); if (DEBUG_CONFIGURATION && configChanged) { Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration()); @@ -1318,31 +1197,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // variables, because mFrameSizeChanged only tracks the width and height changing. mWindowFrames.mLastFrame.set(mWindowFrames.mFrame); - if (mContentInsetsChanged - || mVisibleInsetsChanged - || mStableInsetsChanged + if (didFrameInsetsChange || winAnimator.mSurfaceResized - || mOutsetsChanged - || mFrameSizeChanged - || mDisplayCutoutChanged || configChanged || dragResizingChanged || mReportOrientationChanged) { if (DEBUG_RESIZE || DEBUG_ORIENTATION) { Slog.v(TAG_WM, "Resize reasons for w=" + this + ": " - + " contentInsetsChanged=" + mContentInsetsChanged - + " " + mContentInsets.toShortString() - + " visibleInsetsChanged=" + mVisibleInsetsChanged - + " " + mVisibleInsets.toShortString() - + " stableInsetsChanged=" + mStableInsetsChanged - + " " + mStableInsets.toShortString() - + " outsetsChanged=" + mOutsetsChanged - + " " + mOutsets.toShortString() + + " " + mWindowFrames.getInsetsChangedInfo() + " surfaceResized=" + winAnimator.mSurfaceResized + " configChanged=" + configChanged + " dragResizingChanged=" + dragResizingChanged - + " reportOrientationChanged=" + mReportOrientationChanged - + " displayCutoutChanged=" + mDisplayCutoutChanged); + + " reportOrientationChanged=" + mReportOrientationChanged); } // If it's a dead window left on screen, and the configuration changed, there is nothing @@ -3015,7 +2881,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag()); try { if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this - + ": " + mCompatFrame); + + ": " + mWindowFrames.mCompatFrame); final MergedConfiguration mergedConfiguration = new MergedConfiguration(mService.mRoot.getConfiguration(), getMergedOverrideConfiguration()); @@ -3026,11 +2892,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING"); final Rect frame = mWindowFrames.mFrame; - final Rect overscanInsets = mLastOverscanInsets; - final Rect contentInsets = mLastContentInsets; - final Rect visibleInsets = mLastVisibleInsets; - final Rect stableInsets = mLastStableInsets; - final Rect outsets = mLastOutsets; + final Rect overscanInsets = mWindowFrames.mLastOverscanInsets; + final Rect contentInsets = mWindowFrames.mLastContentInsets; + final Rect visibleInsets = mWindowFrames.mLastVisibleInsets; + final Rect stableInsets = mWindowFrames.mLastStableInsets; + final Rect outsets = mWindowFrames.mLastOutsets; final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING; final boolean reportOrientation = mReportOrientationChanged; final int displayId = getDisplayId(); @@ -3061,13 +2927,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } - mOverscanInsetsChanged = false; - mContentInsetsChanged = false; - mVisibleInsetsChanged = false; - mStableInsetsChanged = false; - mOutsetsChanged = false; - mFrameSizeChanged = false; - mDisplayCutoutChanged = false; + mWindowFrames.resetInsetsChanged(); mWinAnimator.mSurfaceResized = false; mReportOrientationChanged = false; } catch (RemoteException e) { @@ -3293,7 +3153,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAttrs.writeToProto(proto, ATTRIBUTES); mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS); mWindowFrames.writeToProto(proto, WINDOW_FRAMES); - mContentInsets.writeToProto(proto, CONTENT_INSETS); mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS); mSurfacePosition.writeToProto(proto, SURFACE_POSITION); mWinAnimator.writeToProto(proto, ANIMATOR); @@ -3307,10 +3166,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility); proto.write(HAS_SURFACE, mHasSurface); proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay()); - mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS); - mVisibleInsets.writeToProto(proto, VISIBLE_INSETS); - mStableInsets.writeToProto(proto, STABLE_INSETS); - mOutsets.writeToProto(proto, OUTSETS); proto.write(REMOVE_ON_EXIT, mRemoveOnExit); proto.write(DESTROYING, mDestroying); proto.write(REMOVED, mRemoved); @@ -3417,21 +3272,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " isReadyForDisplay()=" + isReadyForDisplay() + " mWindowRemovalAllowed=" + mWindowRemovalAllowed); if (mEnforceSizeCompat) { - pw.println(prefix + "mCompatFrame=" + mCompatFrame.toShortString(sTmpSB)); + pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB)); } if (dumpAll) { mWindowFrames.dump(pw, prefix); - pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB) - + " content=" + mContentInsets.toShortString(sTmpSB) - + " visible=" + mVisibleInsets.toShortString(sTmpSB) - + " stable=" + mStableInsets.toShortString(sTmpSB) - + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB) - + " outsets=" + mOutsets.toShortString(sTmpSB)); - pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB) - + " content=" + mLastContentInsets.toShortString(sTmpSB) - + " visible=" + mLastVisibleInsets.toShortString(sTmpSB) - + " stable=" + mLastStableInsets.toShortString(sTmpSB) - + " outset=" + mLastOutsets.toShortString(sTmpSB)); + pw.println(prefix + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB)); } super.dump(pw, prefix, dumpAll); pw.println(prefix + mWinAnimator + ":"); @@ -3531,7 +3376,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) { + private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) { final int pw = containingFrame.width(); final int ph = containingFrame.height(); final Task task = getTask(); @@ -3609,10 +3454,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // We need to make sure we update the CompatFrame as it is used for // cropping decisions, etc, on systems where we lack a decor layer. - mCompatFrame.set(mWindowFrames.mFrame); + mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame); if (mEnforceSizeCompat) { // See comparable block in computeFrameLw. - mCompatFrame.scale(mInvGlobalScale); + mWindowFrames.mCompatFrame.scale(mInvGlobalScale); } } @@ -4322,13 +4167,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // On a different display there is no system decor. Crop the window // by the screen boundaries. // TODO(multi-display) - policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height()); - policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top, - displayInfo.logicalWidth - mCompatFrame.left, - displayInfo.logicalHeight - mCompatFrame.top); + policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(), + mWindowFrames.mCompatFrame.height()); + policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top, + displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left, + displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top); } else if (skipDecorCrop()) { // Windows without policy decor aren't cropped. - policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height()); + policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(), + mWindowFrames.mCompatFrame.height()); } else { // Crop to the system decor specified by policy. calculateSystemDecorRect(policyCrop); @@ -4486,12 +4333,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * Updates the last inset values to the current ones. */ void updateLastInsetValues() { - mLastOverscanInsets.set(mOverscanInsets); - mLastContentInsets.set(mContentInsets); - mLastVisibleInsets.set(mVisibleInsets); - mLastStableInsets.set(mStableInsets); - mLastOutsets.set(mOutsets); - mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout; + mWindowFrames.updateLastInsetValues(); } void startAnimation(Animation anim) { @@ -4880,6 +4722,44 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + /** + * Copy the inset values over so they can be sent back to the client when a relayout occurs. + */ + void getInsetsForRelayout(Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, + Rect outStableInsets, Rect outOutsets) { + outOverscanInsets.set(mWindowFrames.mOverscanInsets); + outContentInsets.set(mWindowFrames.mContentInsets); + outVisibleInsets.set(mWindowFrames.mVisibleInsets); + outStableInsets.set(mWindowFrames.mStableInsets); + outOutsets.set(mWindowFrames.mOutsets); + + mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets); + } + + void getContentInsets(Rect outContentInsets) { + outContentInsets.set(mWindowFrames.mContentInsets); + } + + Rect getContentInsets() { + return mWindowFrames.mContentInsets; + } + + void getStableInsets(Rect outStableInsets) { + outStableInsets.set(mWindowFrames.mStableInsets); + } + + Rect getStableInsets() { + return mWindowFrames.mStableInsets; + } + + void resetLastContentInsets() { + mWindowFrames.resetLastContentInsets(); + } + + Rect getVisibleInsets() { + return mWindowFrames.mVisibleInsets; + } + private final class MoveAnimationSpec implements AnimationSpec { private final long mDuration; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index b158ae2840dc..c80eb868a732 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_NONE; + import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; @@ -46,7 +47,6 @@ import static com.android.server.wm.WindowStateAnimatorProto.LAST_CLIP_RECT; import static com.android.server.wm.WindowStateAnimatorProto.SURFACE; import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT; import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE; -import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation; import android.content.Context; import android.graphics.Matrix; @@ -476,8 +476,7 @@ class WindowStateAnimator { flags |= SurfaceControl.SECURE; } - mTmpSize.set(0, 0, 0, 0); - calculateSurfaceBounds(w, attrs); + calculateSurfaceBounds(w, attrs, mTmpSize); final int width = mTmpSize.width(); final int height = mTmpSize.height(); @@ -556,44 +555,38 @@ class WindowStateAnimator { return mSurfaceController; } - private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) { + private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) { + outSize.setEmpty(); if ((attrs.flags & FLAG_SCALED) != 0) { // For a scaled surface, we always want the requested size. - mTmpSize.right = mTmpSize.left + w.mRequestedWidth; - mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight; + outSize.right = w.mRequestedWidth; + outSize.bottom = w.mRequestedHeight; } else { // When we're doing a drag-resizing, request a surface that's fullscreen size, // so that we don't need to reallocate during the process. This also prevents // buffer drops due to size mismatch. if (w.isDragResizing()) { - if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) { - mTmpSize.left = 0; - mTmpSize.top = 0; - } final DisplayInfo displayInfo = w.getDisplayInfo(); - mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth; - mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight; + outSize.right = displayInfo.logicalWidth; + outSize.bottom = displayInfo.logicalHeight; } else { - mTmpSize.right = mTmpSize.left + w.mCompatFrame.width(); - mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height(); + w.getCompatFrameSize(outSize); } } // Something is wrong and SurfaceFlinger will not like this, try to revert to sane values. // This doesn't necessarily mean that there is an error in the system. The sizes might be // incorrect, because it is before the first layout or draw. - if (mTmpSize.width() < 1) { - mTmpSize.right = mTmpSize.left + 1; + if (outSize.width() < 1) { + outSize.right = 1; } - if (mTmpSize.height() < 1) { - mTmpSize.bottom = mTmpSize.top + 1; + if (outSize.height() < 1) { + outSize.bottom = 1; } // Adjust for surface insets. - mTmpSize.left -= attrs.surfaceInsets.left; - mTmpSize.top -= attrs.surfaceInsets.top; - mTmpSize.right += attrs.surfaceInsets.right; - mTmpSize.bottom += attrs.surfaceInsets.bottom; + outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top, + -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom); } boolean hasSurface() { @@ -870,8 +863,7 @@ class WindowStateAnimator { final LayoutParams attrs = mWin.getAttrs(); final Task task = w.getTask(); - mTmpSize.set(0, 0, 0, 0); - calculateSurfaceBounds(w, attrs); + calculateSurfaceBounds(w, attrs, mTmpSize); mExtraHScale = (float) 1.0; mExtraVScale = (float) 1.0; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c131858d3ffe..d1e16c8f2267 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -377,6 +377,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final Set<String> GLOBAL_SETTINGS_WHITELIST; private static final Set<String> GLOBAL_SETTINGS_DEPRECATED; private static final Set<String> SYSTEM_SETTINGS_WHITELIST; + private static final Set<Integer> DA_DISALLOWED_POLICIES; static { SECURE_SETTINGS_WHITELIST = new ArraySet<>(); SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD); @@ -408,6 +409,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS); SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE); SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT); + + DA_DISALLOWED_POLICIES = new ArraySet<>(); + DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); + DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES); + DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD); + DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); } /** @@ -2607,6 +2614,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = UserHandle.getUserId(callingUid); final DevicePolicyData policy = getUserData(userId); ActiveAdmin admin = policy.mAdminMap.get(who); + final boolean isDeviceOwner = isDeviceOwner(admin.info.getComponent(), userId); + final boolean isProfileOwner = isProfileOwner(admin.info.getComponent(), userId); + if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { throw new SecurityException("Admin " + admin.info.getComponent() + " does not own the device"); @@ -2615,6 +2625,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new SecurityException("Admin " + admin.info.getComponent() + " does not own the profile"); } + if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) { + throw new SecurityException("Admin " + admin.info.getComponent() + + " is not a device owner or profile owner, so may not use policy: " + + admin.info.getTagForPolicy(reqPolicy)); + } throw new SecurityException("Admin " + admin.info.getComponent() + " did not specify uses-policy for: " + admin.info.getTagForPolicy(reqPolicy)); @@ -2694,7 +2709,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // DO always has the PO power. return ownsDevice || ownsProfile; } else { - return admin.info.usesPolicy(reqPolicy); + boolean allowedToUsePolicy = ownsDevice || ownsProfile + || !DA_DISALLOWED_POLICIES.contains(reqPolicy) + || getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.Q; + return allowedToUsePolicy && admin.info.usesPolicy(reqPolicy); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index b9f8fdbc7674..0b6a33f8c013 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -883,7 +883,8 @@ public final class SystemServer { } traceBeginAndSlog("StartAlarmManagerService"); - mSystemServiceManager.startService(AlarmManagerService.class); + mSystemServiceManager.startService(new AlarmManagerService(context)); + traceEnd(); traceBeginAndSlog("InitWatchdog"); diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk index 3d7fdbdd7436..2691701f79af 100644 --- a/services/robotests/Android.mk +++ b/services/robotests/Android.mk @@ -63,7 +63,9 @@ LOCAL_SRC_FILES := \ $(call all-Iaidl-files-under, ../../core/java/android/app/backup) \ ../../core/java/android/content/pm/PackageInfo.java \ ../../core/java/android/app/IBackupAgent.aidl \ - ../../core/java/android/util/KeyValueSettingObserver.java + ../../core/java/android/util/KeyValueSettingObserver.java \ + ../../core/java/android/content/pm/PackageParser.java \ + ../../core/java/android/content/pm/SigningInfo.java LOCAL_AIDL_INCLUDES := \ $(call all-Iaidl-files-under, $(INTERNAL_BACKUP)) \ diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java new file mode 100644 index 000000000000..112e1e385fed --- /dev/null +++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java @@ -0,0 +1,495 @@ +package com.android.server.backup.fullbackup; + +import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME; +import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION; +import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME; +import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION; +import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN; + +import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.Shadows.shadowOf; +import static org.testng.Assert.expectThrows; + +import android.annotation.Nullable; +import android.app.Application; +import android.app.backup.BackupDataInput; +import android.app.backup.FullBackupDataOutput; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser.SigningDetails; +import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.os.Build; +import android.os.Build.VERSION_CODES; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.UserHandle; + +import com.android.server.testing.FrameworkRobolectricTestRunner; +import com.android.server.testing.SystemLoaderClasses; +import com.android.server.testing.SystemLoaderPackages; +import com.android.server.testing.shadows.ShadowBackupDataInput; +import com.android.server.testing.shadows.ShadowBackupDataOutput; +import com.android.server.testing.shadows.ShadowFullBackup; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.attribute.FileTime; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplicationPackageManager; +import org.robolectric.shadows.ShadowEnvironment; + +@RunWith(FrameworkRobolectricTestRunner.class) +@Config( + manifest = Config.NONE, + sdk = 26, + shadows = { + ShadowBackupDataInput.class, + ShadowBackupDataOutput.class, + ShadowEnvironment.class, + ShadowFullBackup.class, + }) +@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"}) +@SystemLoaderClasses({PackageInfo.class, SigningInfo.class}) +public class AppMetadataBackupWriterTest { + private static final String TEST_PACKAGE = "com.test.package"; + private static final String TEST_PACKAGE_INSTALLER = "com.test.package.installer"; + private static final Long TEST_PACKAGE_VERSION_CODE = 100L; + + private ShadowApplicationPackageManager mShadowPackageManager; + private File mFilesDir; + private File mBackupDataOutputFile; + private AppMetadataBackupWriter mBackupWriter; + + @Before + public void setUp() throws Exception { + Application application = RuntimeEnvironment.application; + + PackageManager packageManager = application.getPackageManager(); + mShadowPackageManager = (ShadowApplicationPackageManager) shadowOf(packageManager); + + mFilesDir = RuntimeEnvironment.application.getFilesDir(); + mBackupDataOutputFile = new File(mFilesDir, "output"); + mBackupDataOutputFile.createNewFile(); + ParcelFileDescriptor pfd = + ParcelFileDescriptor.open( + mBackupDataOutputFile, ParcelFileDescriptor.MODE_READ_WRITE); + FullBackupDataOutput output = + new FullBackupDataOutput(pfd, /* quota */ -1, /* transportFlags */ 0); + mBackupWriter = new AppMetadataBackupWriter(output, packageManager); + } + + @After + public void tearDown() throws Exception { + mBackupDataOutputFile.delete(); + } + + /** + * The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + @Test + public void testBackupManifest_withoutApkOrSignatures_writesCorrectData() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + + byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(manifest.length).isEqualTo(7); + assertThat(manifest[0]).isEqualTo(Integer.toString(BACKUP_MANIFEST_VERSION)); + assertThat(manifest[1]).isEqualTo(TEST_PACKAGE); + assertThat(manifest[2]).isEqualTo(Long.toString(TEST_PACKAGE_VERSION_CODE)); + assertThat(manifest[3]).isEqualTo(Integer.toString(Build.VERSION.SDK_INT)); + assertThat(manifest[4]).isEqualTo(TEST_PACKAGE_INSTALLER); + assertThat(manifest[5]).isEqualTo("0"); // withApk + assertThat(manifest[6]).isEqualTo("0"); // signatures + manifestFile.delete(); + } + + /** + * The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + @Test + public void testBackupManifest_withApk_writesApk() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ true); + + byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(manifest.length).isEqualTo(7); + assertThat(manifest[5]).isEqualTo("1"); // withApk + manifestFile.delete(); + } + + /** + * The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + @Test + public void testBackupManifest_withSignatures_writesCorrectSignatures() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + packageInfo.signingInfo = + new SigningInfo( + new SigningDetails( + new Signature[] {new Signature("1234"), new Signature("5678")}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3, + null, + null, + null)); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + + byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(manifest.length).isEqualTo(9); + assertThat(manifest[6]).isEqualTo("2"); // # of signatures + assertThat(manifest[7]).isEqualTo("1234"); // first signature + assertThat(manifest[8]).isEqualTo("5678"); // second signature + manifestFile.delete(); + } + + /** + * The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + @Config(sdk = VERSION_CODES.O) + @Test + public void testBackupManifest_whenApiO_writesCorrectApi() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + + byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(manifest.length).isEqualTo(7); + assertThat(manifest[3]).isEqualTo(Integer.toString(VERSION_CODES.O)); // platform version + manifestFile.delete(); + } + + /** + * The manifest format is: + * + * <pre> + * BACKUP_MANIFEST_VERSION + * package name + * package version code + * platform version code + * installer package name (can be empty) + * boolean (1 if archive includes .apk, otherwise 0) + * # of signatures N + * N* (signature byte array in ascii format per Signature.toCharsString()) + * </pre> + */ + @Test + public void testBackupManifest_withoutInstallerPackage_writesEmptyInstaller() throws Exception { + PackageInfo packageInfo = createPackageInfo(TEST_PACKAGE, null, TEST_PACKAGE_VERSION_CODE); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + + byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(manifest.length).isEqualTo(7); + assertThat(manifest[4]).isEqualTo(""); // installer package name + manifestFile.delete(); + } + + @Test + public void testBackupManifest_whenRunPreviouslyWithSameData_producesSameBytesOnSecondRun() + throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File manifestFile = createFile(BACKUP_MANIFEST_FILENAME); + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + byte[] firstRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true); + // Simulate modifying the manifest file to ensure that file metadata does not change the + // backup bytes produced. + modifyFileMetadata(manifestFile); + + mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false); + + byte[] secondRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true); + assertThat(firstRunBytes).isEqualTo(secondRunBytes); + manifestFile.delete(); + } + + /** + * The widget data format with metadata is: + * + * <pre> + * BACKUP_METADATA_VERSION + * package name + * 4 : Integer token identifying the widget data blob. + * 4 : Integer size of the widget data. + * N : Raw bytes of the widget data. + * </pre> + */ + @Test + public void testBackupWidget_writesCorrectData() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File metadataFile = createFile(BACKUP_METADATA_FILENAME); + byte[] widgetBytes = "widget".getBytes(); + + mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes); + + byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + String[] widgetData = new String(writtenBytes, StandardCharsets.UTF_8).split("\n"); + assertThat(widgetData.length).isEqualTo(3); + // Metadata header + assertThat(widgetData[0]).isEqualTo(Integer.toString(BACKUP_METADATA_VERSION)); + assertThat(widgetData[1]).isEqualTo(packageInfo.packageName); + // Widget data + ByteArrayOutputStream expectedBytes = new ByteArrayOutputStream(); + DataOutputStream stream = new DataOutputStream(expectedBytes); + stream.writeInt(BACKUP_WIDGET_METADATA_TOKEN); + stream.writeInt(widgetBytes.length); + stream.write(widgetBytes); + stream.flush(); + assertThat(widgetData[2]).isEqualTo(expectedBytes.toString()); + metadataFile.delete(); + } + + @Test + public void testBackupWidget_withNullWidgetData_throwsNullPointerException() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File metadataFile = createFile(BACKUP_METADATA_FILENAME); + + expectThrows( + NullPointerException.class, + () -> + mBackupWriter.backupWidget( + packageInfo, metadataFile, mFilesDir, /* widgetData */ null)); + + metadataFile.delete(); + } + + @Test + public void testBackupWidget_withEmptyWidgetData_throwsIllegalArgumentException() + throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File metadataFile = createFile(BACKUP_METADATA_FILENAME); + + expectThrows( + IllegalArgumentException.class, + () -> + mBackupWriter.backupWidget( + packageInfo, metadataFile, mFilesDir, new byte[0])); + + metadataFile.delete(); + } + + @Test + public void testBackupWidget_whenRunPreviouslyWithSameData_producesSameBytesOnSecondRun() + throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File metadataFile = createFile(BACKUP_METADATA_FILENAME); + byte[] widgetBytes = "widget".getBytes(); + mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes); + byte[] firstRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true); + // Simulate modifying the metadata file to ensure that file metadata does not change the + // backup bytes produced. + modifyFileMetadata(metadataFile); + + mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes); + + byte[] secondRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true); + assertThat(firstRunBytes).isEqualTo(secondRunBytes); + metadataFile.delete(); + } + + @Test + public void testBackupApk_writesCorrectBytesToOutput() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + byte[] apkBytes = "apk".getBytes(); + File apkFile = createApkFileAndWrite(apkBytes); + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.sourceDir = apkFile.getPath(); + + mBackupWriter.backupApk(packageInfo); + + byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + assertThat(writtenBytes).isEqualTo(apkBytes); + apkFile.delete(); + } + + @Test + public void testBackupObb_withObbData_writesCorrectBytesToOutput() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File obbDir = createObbDirForPackage(packageInfo.packageName); + byte[] obbBytes = "obb".getBytes(); + File obbFile = createObbFileAndWrite(obbDir, obbBytes); + + mBackupWriter.backupObb(packageInfo); + + byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false); + assertThat(writtenBytes).isEqualTo(obbBytes); + obbFile.delete(); + } + + @Test + public void testBackupObb_withNoObbData_doesNotWriteBytesToOutput() throws Exception { + PackageInfo packageInfo = + createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE); + File obbDir = createObbDirForPackage(packageInfo.packageName); + // No obb file created. + + mBackupWriter.backupObb(packageInfo); + + assertThat(mBackupDataOutputFile.length()).isEqualTo(0); + } + + /** + * Creates a test package and registers it with the package manager. Also sets the installer + * package name if not {@code null}. + */ + private PackageInfo createPackageInfo( + String packageName, @Nullable String installerPackageName, long versionCode) { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = packageName; + packageInfo.setLongVersionCode(versionCode); + mShadowPackageManager.addPackage(packageInfo); + if (installerPackageName != null) { + mShadowPackageManager.setInstallerPackageName(packageName, installerPackageName); + } + return packageInfo; + } + + /** + * Reads backup data written to the {@code file} by {@link ShadowBackupDataOutput}. Uses {@link + * ShadowBackupDataInput} to parse the data. Follows the format used by {@link + * ShadowFullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}. + * + * @param includeTarHeader If {@code true}, returns the TAR header and data bytes combined. + * Otherwise, only returns the data bytes. + */ + private byte[] getWrittenBytes(File file, boolean includeTarHeader) throws IOException { + BackupDataInput input = new BackupDataInput(new FileInputStream(file).getFD()); + input.readNextHeader(); + int dataSize = input.getDataSize(); + + byte[] bytes; + if (includeTarHeader) { + bytes = new byte[dataSize + 512]; + input.readEntityData(bytes, 0, dataSize + 512); + } else { + input.readEntityData(new byte[512], 0, 512); // skip TAR header + bytes = new byte[dataSize]; + input.readEntityData(bytes, 0, dataSize); + } + + return bytes; + } + + private File createFile(String fileName) throws IOException { + File file = new File(mFilesDir, fileName); + file.createNewFile(); + return file; + } + + /** + * Sets the last modified time of the {@code file} to the current time to edit the file's + * metadata. + */ + private void modifyFileMetadata(File file) throws IOException { + Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(System.currentTimeMillis())); + } + + private File createApkFileAndWrite(byte[] data) throws IOException { + File apkFile = new File(mFilesDir, "apk"); + apkFile.createNewFile(); + Files.write(apkFile.toPath(), data); + return apkFile; + } + + /** Creates an .obb file in the input directory. */ + private File createObbFileAndWrite(File obbDir, byte[] data) throws IOException { + File obbFile = new File(obbDir, "obb"); + obbFile.createNewFile(); + Files.write(obbFile.toPath(), data); + return obbFile; + } + + /** + * Creates a package specific obb data directory since the backup method checks for obb data + * there. See {@link Environment#buildExternalStorageAppObbDirs(String)}. + */ + private File createObbDirForPackage(String packageName) { + ShadowEnvironment.addExternalDir("test"); + Environment.UserEnvironment userEnv = + new Environment.UserEnvironment(UserHandle.USER_SYSTEM); + File obbDir = + new File( + userEnv.getExternalDirs()[0], + Environment.DIR_ANDROID + "/obb/" + packageName); + obbDir.mkdirs(); + return obbDir; + } +} diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java index ca0400832345..5812c3c85a58 100644 --- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java @@ -55,6 +55,11 @@ public class ShadowBackupDataOutput { return mTransportFlags; } + public ObjectOutputStream getOutputStream() { + ensureOutput(); + return mOutput; + } + @Implementation public int writeEntityHeader(String key, int dataSize) throws IOException { ensureOutput(); diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java b/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java new file mode 100644 index 000000000000..3c913e317375 --- /dev/null +++ b/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java @@ -0,0 +1,70 @@ +package com.android.server.testing.shadows; + +import android.app.backup.BackupDataOutput; +import android.app.backup.FullBackup; +import android.app.backup.FullBackupDataOutput; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadow.api.Shadow; + +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Shadow for {@link FullBackup}. Used to emulate the native method {@link + * FullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}. Relies on + * the shadow {@link ShadowBackupDataOutput}, which must be included in tests that use this shadow. + */ +@Implements(FullBackup.class) +public class ShadowFullBackup { + /** + * Reads data from the specified file at {@code path} and writes it to the {@code output}. Does + * not match the native implementation, and only partially simulates TAR format. Used solely for + * passing backup data for testing purposes. + * + * <p>Note: Only handles the {@code path} denoting a file and not a directory like the real + * implementation. + */ + @Implementation + public static int backupToTar( + String packageName, + String domain, + String linkdomain, + String rootpath, + String path, + FullBackupDataOutput output) { + BackupDataOutput backupDataOutput = output.getData(); + try { + Path file = Paths.get(path); + byte[] data = Files.readAllBytes(file); + backupDataOutput.writeEntityHeader("key", data.length); + + // Partially simulate TAR header (not all fields included). We use a 512 byte block for + // the header to follow the TAR convention and to have a consistent size block to help + // with separating the header from the data. + ByteBuffer tarBlock = ByteBuffer.wrap(new byte[512]); + String tarPath = "apps/" + packageName + (domain == null ? "" : "/" + domain) + path; + tarBlock.put(tarPath.getBytes()); // file path + tarBlock.putInt(0x1ff); // file mode + tarBlock.putLong(Files.size(file)); // file size + tarBlock.putLong(Files.getLastModifiedTime(file).toMillis()); // last modified time + tarBlock.putInt(0); // file type + + // Write TAR header directly to the BackupDataOutput's output stream. + ShadowBackupDataOutput shadowBackupDataOutput = Shadow.extract(backupDataOutput); + ObjectOutputStream outputStream = shadowBackupDataOutput.getOutputStream(); + outputStream.write(tarBlock.array()); + outputStream.flush(); + + backupDataOutput.writeEntityData(data, data.length); + } catch (IOException e) { + throw new AssertionError(e); + } + return 0; + } +} diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk index 34de9dfb7ad6..8c0283318419 100644 --- a/services/tests/mockingservicestests/Android.mk +++ b/services/tests/mockingservicestests/Android.mk @@ -20,22 +20,21 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ - services.core \ - services.devicepolicy \ frameworks-base-testutils \ + services.core \ androidx-test \ mockito-target-extended-minus-junit4 \ + platform-test-annotations \ ShortcutManagerTestUtils \ - compatibility-device-util \ - truth-prebuilt + truth-prebuilt \ -LOCAL_JAVA_LIBRARIES := \ - android.test.mock +LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner LOCAL_JNI_SHARED_LIBRARIES := \ libdexmakerjvmtiagent \ - libstaticjvmtiagent + libstaticjvmtiagent \ +LOCAL_CERTIFICATE := platform LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests LOCAL_PRIVATE_PLATFORM_APIS := true LOCAL_COMPATIBILITY_SUITE := device-tests diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 247e446d1bfc..c9aa63153a5d 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,7 +17,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> - <application android:debuggable="true"> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> + + <application android:testOnly="true" + android:debuggable="true"> <uses-library android:name="android.test.runner" /> </application> diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml index adfee96d4e28..7782d570856f 100644 --- a/services/tests/mockingservicestests/AndroidTest.xml +++ b/services/tests/mockingservicestests/AndroidTest.xml @@ -19,6 +19,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> <option name="test-file-name" value="FrameworksMockingServicesTests.apk" /> </target_preparer> diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java new file mode 100644 index 000000000000..de3d285cd23a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java @@ -0,0 +1,374 @@ +/* + * 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; + +import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; +import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; + +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IUidObserver; +import android.app.PendingIntent; +import android.app.usage.UsageStatsManagerInternal; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.SystemClock; +import android.os.UserHandle; +import android.util.Log; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoSession; + +import javax.annotation.concurrent.GuardedBy; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AlarmManagerServiceTest { + private static final String TAG = AlarmManagerServiceTest.class.getSimpleName(); + private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package"; + private static final int SYSTEM_UI_UID = 123456789; + private static final int TEST_CALLING_UID = 12345; + private static final long DEFAULT_TIMEOUT = 5_000; + + private AlarmManagerService mService; + @Mock + private IActivityManager mIActivityManager; + @Mock + private UsageStatsManagerInternal mUsageStatsManagerInternal; + @Mock + private AppStateTracker mAppStateTracker; + @Mock + private AlarmManagerService.ClockReceiver mClockReceiver; + @Mock + private PowerManager.WakeLock mWakeLock; + + private MockitoSession mMockingSession; + private Injector mInjector; + private volatile long mNowElapsedTest; + @GuardedBy("mTestTimer") + private TestTimer mTestTimer = new TestTimer(); + + static class TestTimer { + private long mElapsed; + boolean mExpired; + + synchronized long getElapsed() { + return mElapsed; + } + + synchronized void set(long millisElapsed) { + mElapsed = millisElapsed; + } + + synchronized long expire() { + mExpired = true; + notify(); + return mElapsed; + } + } + + public class Injector extends AlarmManagerService.Injector { + Injector(Context context) { + super(context); + } + + @Override + void init() { + // Do nothing. + } + + @Override + int waitForAlarm() { + synchronized (mTestTimer) { + if (!mTestTimer.mExpired) { + try { + mTestTimer.wait(); + } catch (InterruptedException ie) { + Log.e(TAG, "Wait interrupted!", ie); + return 0; + } + } + mTestTimer.mExpired = false; + } + return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate. + } + + @Override + void setKernelTimezone(int minutesWest) { + // Do nothing. + } + + @Override + void setAlarm(int type, long millis) { + mTestTimer.set(millis); + } + + @Override + void setKernelTime(long millis) { + } + + @Override + int getSystemUiUid() { + return SYSTEM_UI_UID; + } + + @Override + boolean isAlarmDriverPresent() { + // Pretend the driver is present, so code does not fall back to handler + return true; + } + + @Override + long getElapsedRealtime() { + return mNowElapsedTest; + } + + @Override + AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) { + return mClockReceiver; + } + + @Override + PowerManager.WakeLock getAlarmWakeLock() { + return mWakeLock; + } + } + + @Before + public final void setUp() throws Exception { + mMockingSession = mockitoSession() + .initMocks(this) + .mockStatic(ActivityManager.class, Answers.CALLS_REAL_METHODS) + .mockStatic(LocalServices.class) + .mockStatic(Looper.class, Answers.CALLS_REAL_METHODS) + .startMocking(); + doReturn(mIActivityManager).when(ActivityManager::getService); + doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class)); + doReturn(null) + .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class)); + doReturn(mUsageStatsManagerInternal).when( + () -> LocalServices.getService(UsageStatsManagerInternal.class)); + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong())) + .thenReturn(STANDBY_BUCKET_ACTIVE); + doReturn(Looper.getMainLooper()).when(Looper::myLooper); + + final Context context = InstrumentationRegistry.getTargetContext(); + mInjector = spy(new Injector(context)); + mService = new AlarmManagerService(context, mInjector); + spyOn(mService); + doNothing().when(mService).publishBinderService(any(), any()); + mService.onStart(); + mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); + mService.mConstants.MIN_FUTURITY = 0; + + assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID); + assertEquals(mService.mClockReceiver, mClockReceiver); + assertEquals(mService.mWakeLock, mWakeLock); + verify(mIActivityManager).registerUidObserver(any(IUidObserver.class), anyInt(), anyInt(), + isNull()); + } + + private void setTestAlarm(int type, long triggerTime, PendingIntent operation) { + mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0, + operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null, + TEST_CALLING_UID, TEST_CALLING_PACKAGE); + } + + private PendingIntent getNewMockPendingIntent() { + final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS); + when(mockPi.getCreatorUid()).thenReturn(TEST_CALLING_UID); + when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE); + return mockPi; + } + + @Test + public void testSingleAlarmSet() { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime); + assertEquals(triggerTime, mTestTimer.getElapsed()); + } + + @Test + public void testSingleAlarmExpiration() throws Exception { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + + mNowElapsedTest = mTestTimer.expire(); + + final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor = + ArgumentCaptor.forClass(PendingIntent.OnFinished.class); + verify(alarmPi, timeout(DEFAULT_TIMEOUT)).send(any(Context.class), eq(0), + any(Intent.class), onFinishedCaptor.capture(), any(Handler.class), isNull(), any()); + verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).acquire(); + onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null); + verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).release(); + } + + @Test + public void testMinFuturity() { + mService.mConstants.MIN_FUTURITY = 10; + final long triggerTime = mNowElapsedTest + 1; + final long expectedTriggerTime = mNowElapsedTest + mService.mConstants.MIN_FUTURITY; + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, getNewMockPendingIntent()); + verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, expectedTriggerTime); + } + + @Test + public void testEarliestAlarmSet() { + final PendingIntent pi6 = getNewMockPendingIntent(); + final PendingIntent pi8 = getNewMockPendingIntent(); + final PendingIntent pi9 = getNewMockPendingIntent(); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, pi8); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 9, pi9); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6); + assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed()); + + mService.removeLocked(pi6, null); + assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed()); + + mService.removeLocked(pi8, null); + assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed()); + } + + @Test + public void testStandbyBucketDelay_workingSet() throws Exception { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @Test + public void testStandbyBucketDelay_frequent() { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @Test + public void testStandbyBucketDelay_rare() { + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent()); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent()); + assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed()); + + when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(), + anyLong())).thenReturn(STANDBY_BUCKET_RARE); + mNowElapsedTest = mTestTimer.expire(); + verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce()) + .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), + eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()); + final long expectedNextTrigger = mNowElapsedTest + + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE); + assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: " + + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT, + () -> (mTestTimer.getElapsed() == expectedNextTrigger))); + } + + @After + public void tearDown() { + if (mMockingSession != null) { + mMockingSession.finishMocking(); + } + } + + private boolean pollingCheck(long timeout, Condition condition) { + final long deadline = SystemClock.uptimeMillis() + timeout; + boolean interrupted = false; + while (!condition.check() && SystemClock.uptimeMillis() < deadline) { + try { + Thread.sleep(500); + } catch (InterruptedException ie) { + interrupted = true; + } + } + if (interrupted) { + Thread.currentThread().interrupt(); + } + return condition.check(); + } + + @FunctionalInterface + interface Condition { + boolean check(); + } +} diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 43f319e8ccd6..80307eebf53b 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -9,7 +9,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests # Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) \ + $(call all-java-files-under, utils) \ LOCAL_STATIC_JAVA_LIBRARIES := \ frameworks-base-testutils \ diff --git a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java index 1f63d6165617..d248b8902e35 100644 --- a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java @@ -35,14 +35,13 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) -public class AlarmManagerServiceTest { +public class BackgroundRestrictedAlarmsTest { private SparseArray<ArrayList<Alarm>> addPendingAlarm( SparseArray<ArrayList<Alarm>> all, int uid, String name, boolean removeIt) { ArrayList<Alarm> uidAlarms = all.get(uid); if (uidAlarms == null) { all.put(uid, uidAlarms = new ArrayList<>()); } - // Details don't matter. uidAlarms.add(new Alarm( removeIt ? RTC : RTC_WAKEUP, 0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name)); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java index 360ccbf874f0..a3decb93a9e7 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java @@ -20,6 +20,8 @@ import android.accessibilityservice.FingerprintGestureController; import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback; import android.accessibilityservice.IAccessibilityServiceConnection; import android.os.Looper; +import android.support.test.filters.FlakyTest; + import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -95,6 +97,7 @@ public class FingerprintGestureControllerTest { } @Test + @FlakyTest public void testDetectionActiveCallback_withHandler_shouldPostRunnableToHandler() { MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> { message.getCallback().run(); @@ -142,6 +145,7 @@ public class FingerprintGestureControllerTest { } @Test + @FlakyTest public void testGestureCallback_withHandler_shouldPostRunnableToHandler() { MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> { message.getCallback().run(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java index f9d264b7e532..d6d21c63b1c5 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java @@ -43,6 +43,7 @@ import android.graphics.Region; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.support.test.filters.FlakyTest; import android.view.MagnificationSpec; import androidx.test.runner.AndroidJUnit4; @@ -66,6 +67,7 @@ import org.mockito.stubbing.Answer; import java.util.Locale; @RunWith(AndroidJUnit4.class) +@FlakyTest public class MagnificationControllerTest { static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 100, 200); static final PointF INITIAL_MAGNIFICATION_BOUNDS_CENTER = new PointF( diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java index 59b08909bb76..1023bc1e3b0c 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java @@ -189,7 +189,7 @@ public class ActivityStackTests extends ActivityTestsBase { assertEquals(task.getTopActivity(false /* includeOverlays */), r); assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay); - assertNotNull(result.r); + assertNotNull(result.mRecord); } @Test diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index e37a6c10409d..d94a5f34517f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -1713,24 +1713,35 @@ public class DevicePolicyManagerTest extends DpmTestBase { UserManager.DISALLOW_ADD_USER), eq(true), eq(CAMERA_DISABLED_GLOBALLY)); reset(getServices().userManagerInternal); + } - // Set up another DA and let it disable camera. Now DISALLOW_CAMERA will only be applied - // locally. - dpm.setCameraDisabled(admin1, false); - reset(getServices().userManagerInternal); + public void testDaDisallowedPolicies_SecurityException() throws Exception { + mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); + mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL); - setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID); - dpm.setActiveAdmin(admin2, /* replace =*/ false, UserHandle.USER_SYSTEM); - dpm.setCameraDisabled(admin2, true); + setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID); + dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM); - verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions( - eq(UserHandle.USER_SYSTEM), - // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this - MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN, - UserManager.DISALLOW_ADD_USER), - eq(true), eq(CAMERA_DISABLED_LOCALLY)); - reset(getServices().userManagerInternal); - // TODO Make sure restrictions are written to the file. + boolean originalCameraDisabled = dpm.getCameraDisabled(admin1); + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setCameraDisabled(admin1, true)); + assertEquals(originalCameraDisabled, dpm.getCameraDisabled(admin1)); + + int originalKeyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(admin1); + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setKeyguardDisabledFeatures(admin1, + DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)); + assertEquals(originalKeyguardDisabledFeatures, dpm.getKeyguardDisabledFeatures(admin1)); + + long originalPasswordExpirationTimeout = dpm.getPasswordExpirationTimeout(admin1); + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setPasswordExpirationTimeout(admin1, 1234)); + assertEquals(originalPasswordExpirationTimeout, dpm.getPasswordExpirationTimeout(admin1)); + + int originalPasswordQuality = dpm.getPasswordQuality(admin1); + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC)); + assertEquals(originalPasswordQuality, dpm.getPasswordQuality(admin1)); } public void testSetUserRestriction_asPo() { diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java index ea3a3d097dae..e64823085d35 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java @@ -133,15 +133,15 @@ public class WindowFrameTests extends WindowTestsBase { } private void assertContentInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.mContentInsets, left, top, right, bottom); + assertRect(w.getContentInsets(), left, top, right, bottom); } private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.mVisibleInsets, left, top, right, bottom); + assertRect(w.getVisibleInsets(), left, top, right, bottom); } private void assertStableInset(WindowState w, int left, int top, int right, int bottom) { - assertRect(w.mStableInsets, left, top, right, bottom); + assertRect(w.getStableInsets(), left, top, right, bottom); } private void assertFrame(WindowState w, int left, int top, int right, int bottom) { diff --git a/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java b/services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java index 8dabbc4d4356..8dabbc4d4356 100644 --- a/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java index 1222b59e92b9..1222b59e92b9 100644 --- a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java index b200293ee916..b200293ee916 100644 --- a/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk new file mode 100644 index 000000000000..0f8b18ab92cf --- /dev/null +++ b/services/tests/wmtests/Android.mk @@ -0,0 +1,41 @@ +######################################################################### +# Build WmTests package +######################################################################### + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# We only want this apk build for tests. +LOCAL_MODULE_TAGS := tests + +# Include all test java files. +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src) \ + $(call all-java-files-under, ../servicestests/utils) + +LOCAL_STATIC_JAVA_LIBRARIES := \ + androidx-test \ + mockito-target-minus-junit4 \ + platform-test-annotations \ + +LOCAL_JAVA_LIBRARIES := \ + android.test.mock \ + android.test.base \ + android.test.runner \ + +LOCAL_PACKAGE_NAME := WmTests +LOCAL_PRIVATE_PLATFORM_APIS := true +LOCAL_COMPATIBILITY_SUITE := device-tests + +LOCAL_CERTIFICATE := platform + +LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk + +LOCAL_JACK_FLAGS := --multi-dex native +LOCAL_DX_FLAGS := --multi-dex + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml new file mode 100644 index 000000000000..1fb947309028 --- /dev/null +++ b/services/tests/wmtests/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.wmtests"> + + <!-- Uses API introduced in P (28) --> + <uses-sdk + android:minSdkVersion="1" + android:targetSdkVersion="28" /> + + <application android:testOnly="true" /> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Window Manager Tests" + android:targetPackage="com.android.frameworks.wmtests" /> +</manifest> diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml new file mode 100644 index 000000000000..2717ef901216 --- /dev/null +++ b/services/tests/wmtests/AndroidTest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<configuration description="Runs Window Manager Tests."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WmTests.apk" /> + </target_preparer> + + <option name="test-tag" value="WmTests" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.frameworks.wmtests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false" /> + </test> +</configuration> diff --git a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java new file mode 100644 index 000000000000..023e4ab6636f --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java @@ -0,0 +1,46 @@ +/* + * 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 com.android.server.am; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Test; + +import androidx.test.filters.FlakyTest; + +/** + * Dummy test for com.android.server.am. + * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests. + */ +public class DummyAmTests { + + @Presubmit + @Test + public void preSubmitTest() {} + + @FlakyTest + @Presubmit + @Test + public void flakyPreSubmitTest() {} + + @Test + public void postSubmitTest() {} + + @FlakyTest + @Test + public void flakyPostSubmitTest() {} +} diff --git a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java new file mode 100644 index 000000000000..aecb2783badd --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java @@ -0,0 +1,46 @@ +/* + * 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 com.android.server.wm; + +import android.platform.test.annotations.Presubmit; + +import org.junit.Test; + +import androidx.test.filters.FlakyTest; + +/** + * Dummy test for com.android.server.wm + * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests. + */ +public class DummyWmTests { + + @Presubmit + @Test + public void preSubmitTest() {} + + @FlakyTest + @Presubmit + @Test + public void flakyPreSubmitTest() {} + + @Test + public void postSubmitTest() {} + + @FlakyTest + @Test + public void flakyPostSubmitTest() {} +} |