summaryrefslogtreecommitdiff
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/accessibility/TEST_MAPPING105
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java43
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java10
-rw-r--r--services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java19
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java283
-rw-r--r--services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java270
-rw-r--r--services/backup/java/com/android/server/backup/utils/FullBackupUtils.java72
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java406
-rw-r--r--services/core/java/com/android/server/BatteryService.java6
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java70
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java21
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java94
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java68
-rw-r--r--services/core/java/com/android/server/am/ActivityTaskManagerService.java66
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java6
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING22
-rw-r--r--services/core/java/com/android/server/biometrics/face/FaceService.java7
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java2
-rw-r--r--services/core/java/com/android/server/stats/StatsCompanionService.java217
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java45
-rw-r--r--services/core/java/com/android/server/wm/BoundsAnimationController.java18
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java5
-rw-r--r--services/core/java/com/android/server/wm/RemoteAnimationController.java3
-rw-r--r--services/core/java/com/android/server/wm/TEST_MAPPING22
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowFrames.java231
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java10
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java284
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java40
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java20
-rw-r--r--services/java/com/android/server/SystemServer.java3
-rw-r--r--services/robotests/Android.mk4
-rw-r--r--services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java495
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java5
-rw-r--r--services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java70
-rw-r--r--services/tests/mockingservicestests/Android.mk13
-rw-r--r--services/tests/mockingservicestests/AndroidManifest.xml5
-rw-r--r--services/tests/mockingservicestests/AndroidTest.xml1
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java374
-rw-r--r--services/tests/servicestests/Android.mk4
-rw-r--r--services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java (renamed from services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java)3
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java41
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java6
-rw-r--r--services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java (renamed from services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java)0
-rw-r--r--services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java (renamed from services/tests/servicestests/src/com/android/server/testutils/TestHandler.java)0
-rw-r--r--services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java (renamed from services/tests/servicestests/src/com/android/server/testutils/TestUtils.java)0
-rw-r--r--services/tests/wmtests/Android.mk41
-rw-r--r--services/tests/wmtests/AndroidManifest.xml31
-rw-r--r--services/tests/wmtests/AndroidTest.xml32
-rw-r--r--services/tests/wmtests/src/com/android/server/am/DummyAmTests.java46
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java46
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() {}
+}