Merge "Hide UDFPS overlay when onError during enroll"
diff --git a/.gitignore b/.gitignore
index c47cc8b..5018436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
.idea
*.iml
*.sw*
-gen/
\ No newline at end of file
+gen/
+.vscode/
+*.code-workspace
diff --git a/Android.bp b/Android.bp
index 35f97ac..5c0dd63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -532,6 +532,7 @@
"android.hardware.vibrator-V1.2-java",
"android.hardware.vibrator-V1.3-java",
"android.security.apc-java",
+ "android.security.authorization-java",
"android.system.keystore2-java",
"android.system.suspend.control.internal-java",
"cameraprotosnano",
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
index a060ad9..7e7feaf 100644
--- a/apct-tests/perftests/OWNERS
+++ b/apct-tests/perftests/OWNERS
@@ -1,2 +1,11 @@
-timmurray@google.com
+balejs@google.com
+carmenjackson@google.com
+cfijalkovich@google.com
+dualli@google.com
+edgararriaga@google.com
+jpakaravoor@google.com
+kevinjeon@google.com
philipcuadra@google.com
+shombert@google.com
+timmurray@google.com
+wessam@google.com
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 18643ed..4441643 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.app.BroadcastOptions;
+
import com.android.server.deviceidle.IDeviceIdleConstraint;
public interface DeviceIdleInternal {
@@ -32,8 +34,17 @@
void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
long duration, int userId, boolean sync, String reason);
- // duration in milliseconds
- void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
+ /**
+ * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp
+ * allowlist.
+ * @param uid
+ * @param duration duration in milliseconds
+ * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ * @param sync
+ * @param reason
+ */
+ void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
+ @BroadcastOptions.TempAllowListType int type, boolean sync,
String reason);
// duration in milliseconds
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 4b98c32..7aed32c 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -21,6 +21,8 @@
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
+import android.app.BroadcastOptions;
+import android.app.BroadcastOptions.TempAllowListType;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -1941,9 +1943,9 @@
// duration in milliseconds
@Override
- public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
- String reason) {
- addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, sync, reason);
+ public void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
+ @TempAllowListType int type, boolean sync, String reason) {
+ addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason);
}
// duration in milliseconds
@@ -2719,7 +2721,9 @@
long duration, int userId, boolean sync, String reason) {
try {
int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
- addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, sync, reason);
+ addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration,
+ BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync,
+ reason);
} catch (NameNotFoundException e) {
}
}
@@ -2729,7 +2733,7 @@
* app an exemption to access network and acquire wakelocks.
*/
void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid,
- long duration, boolean sync, String reason) {
+ long duration, @TempAllowListType int type, boolean sync, String reason) {
final long timeNow = SystemClock.elapsedRealtime();
boolean informWhitelistChanged = false;
int appId = UserHandle.getAppId(uid);
@@ -2761,7 +2765,7 @@
} catch (RemoteException e) {
}
postTempActiveTimeoutMessage(uid, duration);
- updateTempWhitelistAppIdsLocked(appId, true);
+ updateTempWhitelistAppIdsLocked(uid, true, duration, type);
if (sync) {
informWhitelistChanged = true;
} else {
@@ -2786,8 +2790,7 @@
try {
final int uid = getContext().getPackageManager().getPackageUidAsUser(
packageName, userId);
- final int appId = UserHandle.getAppId(uid);
- removePowerSaveTempWhitelistAppDirectInternal(appId);
+ removePowerSaveTempWhitelistAppDirectInternal(uid);
} catch (NameNotFoundException e) {
}
}
@@ -2821,7 +2824,8 @@
Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow);
}
synchronized (this) {
- Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId);
+ Pair<MutableLong, String> entry =
+ mTempWhitelistAppIdEndTimes.get(appId);
if (entry == null) {
// Nothing to do
return;
@@ -2832,7 +2836,7 @@
} else {
// Need more time
if (DEBUG) {
- Slog.d(TAG, "Time to remove AppId " + appId + ": " + entry.first.value);
+ Slog.d(TAG, "Time to remove uid " + uid + ": " + entry.first.value);
}
postTempActiveTimeoutMessage(uid, entry.first.value - timeNow);
}
@@ -2841,12 +2845,12 @@
@GuardedBy("this")
private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) {
- final int appId = UserHandle.getAppId(uid);
if (DEBUG) {
Slog.d(TAG, "Removing uid " + uid + " from temp whitelist");
}
- updateTempWhitelistAppIdsLocked(appId, false);
- mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0)
+ final int appId = UserHandle.getAppId(uid);
+ updateTempWhitelistAppIdsLocked(uid, false, 0, 0);
+ mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0)
.sendToTarget();
reportTempWhitelistChangedLocked(uid, false);
try {
@@ -3869,7 +3873,16 @@
passWhiteListsToForceAppStandbyTrackerLocked();
}
- private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) {
+ /**
+ * update temp allowlist.
+ * @param uid uid to add or remove from temp allowlist.
+ * @param adding true to add to temp allowlist, false to remove from temp allowlist.
+ * @param durationMs duration in milliseconds to add to temp allowlist, only valid when
+ * param adding is true.
+ * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+ */
+ private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
+ @TempAllowListType int type) {
final int size = mTempWhitelistAppIdEndTimes.size();
if (mTempWhitelistAppIdArray.length != size) {
mTempWhitelistAppIdArray = new int[size];
@@ -3882,8 +3895,8 @@
Slog.d(TAG, "Setting activity manager temp whitelist to "
+ Arrays.toString(mTempWhitelistAppIdArray));
}
- mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, appId,
- adding);
+ mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid,
+ adding, durationMs, type);
}
if (mLocalPowerManager != null) {
if (DEBUG) {
diff --git a/core/api/current.txt b/core/api/current.txt
index e77ada6..5e07790 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12053,7 +12053,6 @@
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
- method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
method public void addChildSessionId(int);
method public void close();
method public void commit(@NonNull android.content.IntentSender);
@@ -12067,6 +12066,7 @@
method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
method public void removeChildSessionId(int);
method public void removeSplit(@NonNull String) throws java.io.IOException;
+ method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
method public void setStagingProgress(float);
method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d4d05f0..275b31b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10231,6 +10231,7 @@
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method public final void resetConnectionTime();
method public void setCallDirection(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10407,6 +10408,7 @@
}
public final class RemoteConnection {
+ method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method @Deprecated public void setAudioState(android.telecom.AudioState);
}
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
index c6f42f7..a31cfae 100644
--- a/core/java/android/accessibilityservice/OWNERS
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -1,4 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
-qasid@google.com
+ryanlwlin@google.com
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f7f42a6..986051c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -98,7 +98,7 @@
public abstract void killForegroundAppsForUser(@UserIdInt int userId);
/**
- * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
+ * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions
* such as Power Save mode.
* @param target
* @param whitelistToken
@@ -132,9 +132,14 @@
/**
* Update information about which app IDs are on the temp whitelist.
+ * @param appids the updated list of appIds in temp allowlist.
+ * @param changingUid uid to add or remove to temp allowlist.
+ * @param adding true to add to temp allowlist, false to remove from temp allowlist.
+ * @param durationMs when adding is true, the duration to be in temp allowlist.
+ * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}.
*/
- public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId,
- boolean adding);
+ public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
+ boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type);
/**
* Get the procstate for the UID. The return value will be between
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a23dd35..161b731 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1645,7 +1645,7 @@
*/
private static int[] sOpToSwitch = new int[] {
OP_COARSE_LOCATION, // COARSE_LOCATION
- OP_COARSE_LOCATION, // FINE_LOCATION
+ OP_FINE_LOCATION, // FINE_LOCATION
OP_COARSE_LOCATION, // GPS
OP_VIBRATE, // VIBRATE
OP_READ_CONTACTS, // READ_CONTACTS
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
new file mode 100644
index 0000000..045c55f
--- /dev/null
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Person;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.net.UriCodec;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class AppSearchPerson extends GenericDocument {
+
+ /** The name of the schema type for {@link Person} documents.*/
+ public static final String SCHEMA_TYPE = "Person";
+
+ public static final String KEY_NAME = "name";
+ public static final String KEY_KEY = "key";
+ public static final String KEY_IS_BOT = "isBot";
+ public static final String KEY_IS_IMPORTANT = "isImportant";
+
+ private AppSearchPerson(@NonNull GenericDocument document) {
+ super(document);
+ }
+
+ public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+ .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_NAME)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_KEY)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_BOT)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_IMPORTANT)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).build();
+
+ /** hide */
+ @NonNull
+ public static AppSearchPerson instance(@NonNull final Person person) {
+ Objects.requireNonNull(person);
+ final String id;
+ if (person.getUri() != null) {
+ id = person.getUri();
+ } else {
+ // NOTE: an identifier is required even when uri is null.
+ id = UUID.randomUUID().toString();
+ }
+ return new Builder(id).setName(person.getName())
+ .setKey(person.getKey()).setIsBot(person.isBot())
+ .setIsImportant(person.isImportant()).build();
+ }
+
+ /** hide */
+ @NonNull
+ public Person toPerson() {
+ String uri;
+ try {
+ uri = UriCodec.decode(
+ getUri(), false /* convertPlus */, StandardCharsets.UTF_8,
+ true /* throwOnFailure */);
+ } catch (IllegalArgumentException e) {
+ uri = null;
+ }
+ return new Person.Builder().setName(getPropertyString(KEY_NAME))
+ .setUri(uri).setKey(getPropertyString(KEY_KEY))
+ .setBot(getPropertyBoolean(KEY_IS_BOT))
+ .setImportant(getPropertyBoolean(KEY_IS_IMPORTANT)).build();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static class Builder extends GenericDocument.Builder<Builder> {
+
+ public Builder(@NonNull final String id) {
+ super(id, SCHEMA_TYPE);
+ }
+
+ /** @hide */
+ @NonNull
+ public Builder setName(@Nullable final CharSequence name) {
+ if (name != null) {
+ setPropertyString(KEY_NAME, name.toString());
+ }
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ public Builder setKey(@Nullable final String key) {
+ if (key != null) {
+ setPropertyString(KEY_KEY, key);
+ }
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ public Builder setIsBot(final boolean isBot) {
+ setPropertyBoolean(KEY_IS_BOT, isBot);
+ return this;
+ }
+
+ /** @hide */
+ @NonNull
+ public Builder setIsImportant(final boolean isImportant) {
+ setPropertyBoolean(KEY_IS_IMPORTANT, isImportant);
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public AppSearchPerson build() {
+ return new AppSearchPerson(super.build());
+ }
+ }
+}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
new file mode 100644
index 0000000..14b8df8
--- /dev/null
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Person;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.LocusId;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class AppSearchShortcutInfo extends GenericDocument {
+
+ /** The name of the schema type for {@link ShortcutInfo} documents.*/
+ public static final String SCHEMA_TYPE = "Shortcut";
+
+ public static final String KEY_PACKAGE_NAME = "packageName";
+ public static final String KEY_ACTIVITY = "activity";
+ public static final String KEY_TITLE = "title";
+ public static final String KEY_TEXT = "text";
+ public static final String KEY_DISABLED_MESSAGE = "disabledMessage";
+ public static final String KEY_CATEGORIES = "categories";
+ public static final String KEY_INTENTS = "intents";
+ public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras";
+ public static final String KEY_PERSON = "person";
+ public static final String KEY_LOCUS_ID = "locusId";
+ public static final String KEY_RANK = "rank";
+ public static final String KEY_EXTRAS = "extras";
+ public static final String KEY_FLAGS = "flags";
+ public static final String KEY_ICON_RES_ID = "iconResId";
+ public static final String KEY_ICON_RES_NAME = "iconResName";
+ public static final String KEY_ICON_URI = "iconUri";
+ public static final String KEY_BITMAP_PATH = "bitmapPath";
+ public static final String KEY_DISABLED_REASON = "disabledReason";
+
+ public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+ .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PACKAGE_NAME)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ACTIVITY)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TITLE)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TEXT)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_MESSAGE)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CATEGORIES)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENTS)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENT_PERSISTABLE_EXTRAS)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PERSON)
+ .setSchemaType(AppSearchPerson.SCHEMA_TYPE)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_LOCUS_ID)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_RANK)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_EXTRAS)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FLAGS)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_ID)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_NAME)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_URI)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BITMAP_PATH)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+ .build()
+
+ ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_REASON)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+ .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+ .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+ .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+ .build()
+
+ ).build();
+
+ public AppSearchShortcutInfo(@NonNull GenericDocument document) {
+ super(document);
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) {
+ Objects.requireNonNull(shortcutInfo);
+ return new Builder(shortcutInfo.getId())
+ .setActivity(shortcutInfo.getActivity())
+ .setPackageName(shortcutInfo.getPackage())
+ .setTitle(shortcutInfo.getShortLabel())
+ .setText(shortcutInfo.getLongLabel())
+ .setDisabledMessage(shortcutInfo.getDisabledMessage())
+ .setCategories(shortcutInfo.getCategories())
+ .setIntents(shortcutInfo.getIntents())
+ .setRank(shortcutInfo.getRank())
+ .setExtras(shortcutInfo.getExtras())
+ .setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp())
+ .setFlags(shortcutInfo.getFlags())
+ .setIconResId(shortcutInfo.getIconResourceId())
+ .setIconResName(shortcutInfo.getIconResName())
+ .setBitmapPath(shortcutInfo.getBitmapPath())
+ .setIconUri(shortcutInfo.getIconUri())
+ .setDisabledReason(shortcutInfo.getDisabledReason())
+ .setPersons(shortcutInfo.getPersons())
+ .setLocusId(shortcutInfo.getLocusId())
+ .build();
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public ShortcutInfo toShortcutInfo() {
+ return toShortcutInfo(UserHandle.myUserId());
+ }
+
+ /**
+ * @hide
+ * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides.
+ */
+ @NonNull
+ public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) {
+ final String packageName = getPropertyString(KEY_PACKAGE_NAME);
+ final String activityString = getPropertyString(KEY_ACTIVITY);
+ final ComponentName activity = activityString == null
+ ? null : ComponentName.unflattenFromString(activityString);
+ // TODO: proper icon handling
+ // NOTE: bitmap based icons are currently saved in side-channel (see ShortcutBitmapSaver),
+ // re-creating Icon object at creation time implies turning this function into async since
+ // loading bitmap is I/O bound. Since ShortcutInfo#getIcon is already annotated with
+ // @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with
+ // LauncherApps#getShortcutIconDrawable instead.
+ final Icon icon = null;
+ final String title = getPropertyString(KEY_TITLE);
+ final String text = getPropertyString(KEY_TEXT);
+ final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE);
+ final String[] categories = getPropertyStringArray(KEY_CATEGORIES);
+ final Set<String> categoriesSet = categories == null
+ ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories));
+ final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS);
+ final Intent[] intents = intentsStrings == null
+ ? null : Arrays.stream(intentsStrings).map(uri -> {
+ try {
+ return Intent.parseUri(uri, /* flags =*/ 0);
+ } catch (URISyntaxException e) {
+ // ignore malformed entry
+ }
+ return null;
+ }).toArray(Intent[]::new);
+ final byte[][] intentExtrasesBytes = getPropertyBytesArray(KEY_INTENT_PERSISTABLE_EXTRAS);
+ final Bundle[] intentExtrases = intentExtrasesBytes == null
+ ? null : Arrays.stream(intentExtrasesBytes)
+ .map(this::transformToBundle).toArray(Bundle[]::new);
+ if (intents != null) {
+ for (int i = 0; i < intents.length; i++) {
+ final Intent intent = intents[i];
+ if (intent != null) {
+ intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]);
+ }
+ }
+ }
+ final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON));
+ final String locusIdString = getPropertyString(KEY_LOCUS_ID);
+ final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString);
+ final int rank = (int) getPropertyLong(KEY_RANK);
+ final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS);
+ final PersistableBundle extras = transformToPersistableBundle(extrasByte);
+ final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS));
+ final int iconResId = (int) getPropertyLong(KEY_ICON_RES_ID);
+ final String iconResName = getPropertyString(KEY_ICON_RES_NAME);
+ final String iconUri = getPropertyString(KEY_ICON_URI);
+ final String bitmapPath = getPropertyString(KEY_BITMAP_PATH);
+ final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON);
+ return new ShortcutInfo(
+ userId, getUri(), packageName, activity, icon, title, 0, null,
+ text, 0, null, disabledMessage, 0, null,
+ categoriesSet, intents, rank, extras,
+ getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
+ disabledReason, persons, locusId);
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public static class Builder extends GenericDocument.Builder<Builder> {
+
+ public Builder(String id) {
+ super(id, SCHEMA_TYPE);
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setLocusId(@Nullable final LocusId locusId) {
+ if (locusId != null) {
+ setPropertyString(KEY_LOCUS_ID, locusId.getId());
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setActivity(@Nullable final ComponentName activity) {
+ if (activity != null) {
+ setPropertyString(KEY_ACTIVITY, activity.flattenToShortString());
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setTitle(@Nullable final CharSequence shortLabel) {
+ if (!TextUtils.isEmpty(shortLabel)) {
+ setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty(
+ shortLabel, "shortLabel cannot be empty").toString());
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setText(@Nullable final CharSequence longLabel) {
+ if (!TextUtils.isEmpty(longLabel)) {
+ setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty(
+ longLabel, "longLabel cannot be empty").toString());
+ }
+ return this;
+
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setDisabledMessage(@Nullable final CharSequence disabledMessage) {
+ if (!TextUtils.isEmpty(disabledMessage)) {
+ setPropertyString(KEY_DISABLED_MESSAGE, Preconditions.checkStringNotEmpty(
+ disabledMessage, "disabledMessage cannot be empty").toString());
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setCategories(@Nullable final Set<String> categories) {
+ if (categories != null && !categories.isEmpty()) {
+ setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new));
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setIntent(@Nullable final Intent intent) {
+ if (intent == null) {
+ return this;
+ }
+ return setIntents(new Intent[]{intent});
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setIntents(@Nullable final Intent[] intents) {
+ if (intents == null || intents.length == 0) {
+ return this;
+ }
+ for (Intent intent : intents) {
+ Objects.requireNonNull(intent, "intents cannot contain null");
+ Objects.requireNonNull(intent.getAction(), "intent's action must be set");
+ }
+ final byte[][] intentExtrases = new byte[intents.length][];
+ for (int i = 0; i < intents.length; i++) {
+ final Intent intent = intents[i];
+ final Bundle extras = intent.getExtras();
+ intentExtrases[i] = extras == null
+ ? new byte[0] : transformToByteArray(new PersistableBundle(extras));
+ }
+
+ setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it ->
+ it.toUri(0)).toArray(String[]::new));
+ setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setPerson(@Nullable final Person person) {
+ if (person == null) {
+ return this;
+ }
+ return setPersons(new Person[]{person});
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setPersons(@Nullable final Person[] persons) {
+ if (persons == null || persons.length == 0) {
+ return this;
+ }
+ setPropertyDocument(KEY_PERSON,
+ Arrays.stream(persons).map(person -> AppSearchPerson.instance(
+ Objects.requireNonNull(person, "persons cannot contain null"))
+ ).toArray(AppSearchPerson[]::new));
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setRank(final int rank) {
+ Preconditions.checkArgument((0 <= rank),
+ "Rank cannot be negative or bigger than MAX_RANK");
+ setPropertyLong(KEY_RANK, rank);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setExtras(@Nullable final PersistableBundle extras) {
+ if (extras != null) {
+ setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras));
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setPackageName(@Nullable final String packageName) {
+ if (!TextUtils.isEmpty(packageName)) {
+ setPropertyString(KEY_PACKAGE_NAME, packageName);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) {
+ setPropertyLong(KEY_FLAGS, flattenFlags(flags));
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ public Builder setIconResId(@Nullable final int iconResId) {
+ setPropertyLong(KEY_ICON_RES_ID, iconResId);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setIconResName(@Nullable final String iconResName) {
+ if (!TextUtils.isEmpty(iconResName)) {
+ setPropertyString(KEY_ICON_RES_NAME, iconResName);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setBitmapPath(@Nullable final String bitmapPath) {
+ if (!TextUtils.isEmpty(bitmapPath)) {
+ setPropertyString(KEY_BITMAP_PATH, bitmapPath);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setIconUri(@Nullable final String iconUri) {
+ if (!TextUtils.isEmpty(iconUri)) {
+ setPropertyString(KEY_ICON_URI, iconUri);
+ }
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setDisabledReason(@ShortcutInfo.DisabledReason final int disabledReason) {
+ setPropertyLong(KEY_DISABLED_REASON, disabledReason);
+ return this;
+ }
+
+ /**
+ * @hide
+ */
+ @NonNull
+ @Override
+ public AppSearchShortcutInfo build() {
+ return new AppSearchShortcutInfo(super.build());
+ }
+ }
+
+ /**
+ * Convert PersistableBundle into byte[] for persistence.
+ */
+ @Nullable
+ private static byte[] transformToByteArray(@NonNull final PersistableBundle extras) {
+ Objects.requireNonNull(extras);
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ new PersistableBundle(extras).writeToStream(baos);
+ return baos.toByteArray();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Convert byte[] into Bundle.
+ */
+ @Nullable
+ private Bundle transformToBundle(@Nullable final byte[] extras) {
+ if (extras == null) {
+ return null;
+ }
+ Objects.requireNonNull(extras);
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) {
+ final Bundle ret = new Bundle();
+ ret.putAll(PersistableBundle.readFromStream(bais));
+ return ret;
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Convert byte[] into PersistableBundle.
+ */
+ @Nullable
+ private PersistableBundle transformToPersistableBundle(@Nullable final byte[] extras) {
+ if (extras == null) {
+ return null;
+ }
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) {
+ return PersistableBundle.readFromStream(bais);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ private static long[] flattenFlags(@ShortcutInfo.ShortcutFlags final int flags) {
+ final List<Integer> flattenedFlags = new ArrayList<>();
+ flattenedFlags.add(0);
+ for (int i = 0; i < 31; i++) {
+ final int mask = 1 << i;
+ if ((flags & mask) != 0) {
+ flattenedFlags.add(mask);
+ }
+ }
+ return flattenedFlags.stream().mapToLong(i -> i).toArray();
+ }
+
+ private static int parseFlags(final long[] flags) {
+ return (int) Arrays.stream(flags).reduce((p, v) -> p | v).getAsLong();
+ }
+
+ @NonNull
+ private static Person[] parsePerson(@Nullable final GenericDocument[] persons) {
+ return persons == null ? new Person[0] : Arrays.stream(persons).map(it ->
+ ((AppSearchPerson) it).toPerson()).toArray(Person[]::new);
+ }
+}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 9a73be9..f72288c 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -35,7 +35,7 @@
void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
void stageViaHardLink(String target);
- void addChecksums(String name, in Checksum[] checksums);
+ void setChecksums(String name, in Checksum[] checksums, in byte[] signature);
void removeSplit(String splitName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 01d4c280..248be0f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1247,12 +1247,15 @@
}
/**
- * Adds installer-provided checksums for the APK file in session.
+ * Sets installer-provided checksums for the APK file in session.
*
* @param name previously written as part of this session.
* {@link #openWrite}
* @param checksums installer intends to make available via
* {@link PackageManager#requestChecksums}.
+ * @param signature PKCS#7 detached signature bytes over serialized checksums to enable
+ * fs-verity for the checksums or null if fs-verity should not be enabled.
+ * @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a>
* @throws SecurityException if called after the session has been
* committed or abandoned.
* @throws IllegalStateException if checksums for this file have already been added.
@@ -1262,13 +1265,14 @@
* in {@link PackageManager#requestChecksums}.
*/
@Deprecated
- public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
- throws IOException {
+ public void setChecksums(@NonNull String name, @NonNull List<Checksum> checksums,
+ @Nullable byte[] signature) throws IOException {
Objects.requireNonNull(name);
Objects.requireNonNull(checksums);
try {
- mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()]));
+ mSession.setChecksums(name, checksums.toArray(new Checksum[checksums.size()]),
+ signature);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index da75fba..522f4ca 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2191,7 +2191,7 @@
dest.writeString8(mIconUri);
}
- public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
+ public static final @NonNull Creator<ShortcutInfo> CREATOR =
new Creator<ShortcutInfo>() {
public ShortcutInfo createFromParcel(Parcel source) {
return new ShortcutInfo(source);
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 04b585c..80ac64b 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -16,6 +16,7 @@
package android.net.vcn;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.os.ParcelUuid;
@@ -25,4 +26,7 @@
interface IVcnManagementService {
void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
void clearVcnConfig(in ParcelUuid subscriptionGroup);
+
+ void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
+ void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
}
diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
new file mode 100644
index 0000000..f8ae492
--- /dev/null
+++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+/** @hide */
+interface IVcnUnderlyingNetworkPolicyListener {
+ void onPolicyChanged();
+}
\ No newline at end of file
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index b881a339..2ccdc26 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -25,7 +25,12 @@
import android.os.RemoteException;
import android.os.ServiceSpecificException;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
/**
* VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
@@ -60,6 +65,11 @@
public final class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
+ @VisibleForTesting
+ public static final Map<
+ VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+ REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
+
@NonNull private final Context mContext;
@NonNull private final IVcnManagementService mService;
@@ -136,4 +146,101 @@
throw e.rethrowFromSystemServer();
}
}
+
+ // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi
+ /**
+ * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
+ * can register to receive updates for VCN-underlying Network policies from the System Server.
+ *
+ * @hide
+ */
+ public interface VcnUnderlyingNetworkPolicyListener {
+ /**
+ * Notifies the implementation that the VCN's underlying Network policy has changed.
+ *
+ * <p>After receiving this callback, implementations MUST poll VcnManager for the updated
+ * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy.
+ */
+ void onPolicyChanged();
+ }
+
+ /**
+ * Add a listener for VCN-underlying network policy updates.
+ *
+ * @param executor the Executor that will be used for invoking all calls to the specified
+ * Listener
+ * @param listener the VcnUnderlyingNetworkPolicyListener to be added
+ * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+ * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is
+ * already registered
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ public void addVcnUnderlyingNetworkPolicyListener(
+ @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(executor, "executor must not be null");
+ requireNonNull(listener, "listener must not be null");
+
+ VcnUnderlyingNetworkPolicyListenerBinder binder =
+ new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener);
+ if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) {
+ throw new IllegalArgumentException(
+ "Attempting to add a listener that is already in use");
+ }
+
+ try {
+ mService.addVcnUnderlyingNetworkPolicyListener(binder);
+ } catch (RemoteException e) {
+ REGISTERED_POLICY_LISTENERS.remove(listener);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager.
+ *
+ * <p>If the specified listener is not currently registered, this is a no-op.
+ *
+ * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed
+ * @hide
+ */
+ public void removeVcnUnderlyingNetworkPolicyListener(
+ @NonNull VcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener must not be null");
+
+ VcnUnderlyingNetworkPolicyListenerBinder binder =
+ REGISTERED_POLICY_LISTENERS.remove(listener);
+ if (binder == null) {
+ return;
+ }
+
+ try {
+ mService.removeVcnUnderlyingNetworkPolicyListener(binder);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
+ * Server.
+ *
+ * @hide
+ */
+ private static class VcnUnderlyingNetworkPolicyListenerBinder
+ extends IVcnUnderlyingNetworkPolicyListener.Stub {
+ @NonNull private final Executor mExecutor;
+ @NonNull private final VcnUnderlyingNetworkPolicyListener mListener;
+
+ private VcnUnderlyingNetworkPolicyListenerBinder(
+ Executor executor, VcnUnderlyingNetworkPolicyListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onPolicyChanged() {
+ mExecutor.execute(() -> mListener.onPolicyChanged());
+ }
+ }
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
new file mode 100644
index 0000000..6cb6ee6
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+/** @hide */
+parcelable VcnUnderlyingNetworkPolicy;
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
new file mode 100644
index 0000000..dd7c86d8
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import android.annotation.NonNull;
+import android.net.NetworkCapabilities;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * VcnUnderlyingNetworkPolicy represents the Network policy for a VCN-managed Network.
+ *
+ * <p>Transports that are bringing up networks capable of acting as a VCN's underlying network
+ * should query for policy state upon major capability changes (e.g. changing of TRUSTED bit), and
+ * when prompted by VcnManagementService via VcnUnderlyingNetworkPolicyListener.
+ *
+ * @hide
+ */
+public final class VcnUnderlyingNetworkPolicy implements Parcelable {
+ private final boolean mIsTearDownRequested;
+ private final NetworkCapabilities mMergedNetworkCapabilities;
+
+ /**
+ * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters.
+ *
+ * @hide
+ */
+ public VcnUnderlyingNetworkPolicy(
+ boolean isTearDownRequested, @NonNull NetworkCapabilities mergedNetworkCapabilities) {
+ Objects.requireNonNull(
+ mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull");
+
+ mIsTearDownRequested = isTearDownRequested;
+ mMergedNetworkCapabilities = mergedNetworkCapabilities;
+ }
+
+ /**
+ * Returns whether this Carrier VCN policy policy indicates that the underlying Network should
+ * be torn down.
+ */
+ public boolean isTeardownRequested() {
+ return mIsTearDownRequested;
+ }
+
+ /**
+ * Returns the NetworkCapabilities with Carrier VCN policy bits merged into the provided
+ * capabilities.
+ */
+ @NonNull
+ public NetworkCapabilities getMergedNetworkCapabilities() {
+ return mMergedNetworkCapabilities;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false;
+ final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o;
+
+ return mIsTearDownRequested == that.mIsTearDownRequested
+ && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBoolean(mIsTearDownRequested);
+ dest.writeParcelable(mMergedNetworkCapabilities, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR =
+ new Creator<VcnUnderlyingNetworkPolicy>() {
+ public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) {
+ return new VcnUnderlyingNetworkPolicy(
+ in.readBoolean(), in.readParcelable(null));
+ }
+
+ public VcnUnderlyingNetworkPolicy[] newArray(int size) {
+ return new VcnUnderlyingNetworkPolicy[size];
+ }
+ };
+}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 59292baa..9fdc72b 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,6 +36,7 @@
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFileParcel;
import android.text.TextUtils;
@@ -70,7 +71,8 @@
@Nullable StorageHealthCheckParams healthCheckParams,
@Nullable IStorageHealthListener healthListener,
@NonNull List<InstallationFileParcel> addedFiles,
- @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
+ IPackageLoadingProgressCallback progressCallback) throws IOException {
// TODO(b/136132412): validity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
@@ -95,7 +97,11 @@
throw new IOException("Unknown file location: " + file.location);
}
}
-
+ // Register progress loading callback after files have been added
+ if (progressCallback != null) {
+ incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
+ progressCallback);
+ }
result.startLoading();
return result;
@@ -180,6 +186,7 @@
try {
mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+ mDefaultStorage.unregisterLoadingProgressListener();
} catch (IOException ignored) {
}
mDefaultStorage = null;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7e0ebbc..f24fbab 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9154,14 +9154,14 @@
* Handles an inbound request for scroll capture from the system. If a client is not already
* active, a search will be dispatched through the view tree to locate scrolling content.
* <p>
- * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect,
+ * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect,
* Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
* depending on the results of the search.
*
* @param callbacks to receive responses
* @see ScrollCaptureTargetResolver
*/
- private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+ public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
// Window (root) level callbacks
@@ -9169,10 +9169,12 @@
// Search through View-tree
View rootView = getView();
- Point point = new Point();
- Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
- getChildVisibleRect(rootView, rect, point);
- rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+ if (rootView != null) {
+ Point point = new Point();
+ Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
+ getChildVisibleRect(rootView, rect, point);
+ rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+ }
// No-op path. Scroll capture not offered for this window.
if (targetList.isEmpty()) {
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
index 93b5a2e..b1d3967 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -9,3 +9,4 @@
ogunwale@google.com
jjaggi@google.com
pweaver@google.com
+ryanlwlin@google.com
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index b9ff26b..6281ee9 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -39,6 +39,7 @@
import android.view.inspector.InspectableProperty;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import java.util.ArrayList;
@@ -776,17 +777,23 @@
/**
* Grows {@code r} from its center such that each dimension is at least {@code minimumSize}.
+ *
+ * The result will still have the same {@link Rect#centerX()} and {@link Rect#centerY()} as the
+ * input.
+ *
+ * @hide
*/
- private void growRectTo(Rect r, int minimumSize) {
- int dy = (minimumSize - r.height()) / 2;
+ @VisibleForTesting
+ public void growRectTo(Rect r, int minimumSize) {
+ int dy = minimumSize - r.height();
if (dy > 0) {
- r.top -= dy;
- r.bottom += dy;
+ r.top -= (dy + 1) / 2;
+ r.bottom += dy / 2;
}
- int dx = (minimumSize - r.width()) / 2;
+ int dx = minimumSize - r.width();
if (dx > 0) {
- r.left -= dx;
- r.right += dx;
+ r.left -= (dx + 1) / 2;
+ r.right += dx / 2;
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 33aa190..f105320 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,6 +19,8 @@
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
+import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -103,7 +105,6 @@
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.power.MeasuredEnergyArray;
import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.power.MeasuredEnergyStats.EnergyBucket;
import com.android.internal.util.ArrayUtils;
@@ -370,14 +371,6 @@
* @param railStats
*/
void fillRailDataStats(RailStats railStats);
- /**
- * Function to get energy consumption data
- *
- * @return an array of measured energy (in microjoules) since boot, will be null if
- * measured energy data is unavailable
- */
- @Nullable
- MeasuredEnergyArray getEnergyConsumptionData();
}
public static abstract class UserInfoProvider {
@@ -10704,15 +10697,13 @@
}
public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
- MeasuredEnergyRetriever energyStatsCb, boolean[] supportedEnergyBuckets,
- UserInfoProvider userInfoProvider) {
- this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, supportedEnergyBuckets,
- userInfoProvider);
+ MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
+ this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider);
}
private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb,
- boolean[] supportedEnergyBuckets, UserInfoProvider userInfoProvider) {
+ UserInfoProvider userInfoProvider) {
init(clocks);
if (systemDir == null) {
@@ -10818,10 +10809,6 @@
// Notify statsd that the system is initially not in doze.
mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
-
- mGlobalMeasuredEnergyStats = supportedEnergyBuckets == null ? null :
- new MeasuredEnergyStats(supportedEnergyBuckets);
- mScreenStateAtLastEnergyMeasurement = mScreenState;
}
@UnsupportedAppUsage
@@ -12503,21 +12490,6 @@
}
/**
- * Get energy consumed (in microjoules) by a set of subsystems from the {@link
- * MeasuredEnergyRetriever}, if available.
- *
- * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is
- * unavailable.
- */
- @Nullable
- public MeasuredEnergyArray getEnergyConsumptionDataLocked() {
- if (mMeasuredEnergyRetriever == null) {
- return null;
- }
- return mMeasuredEnergyRetriever.getEnergyConsumptionData();
- }
-
- /**
* Read and distribute kernel wake lock use across apps.
*/
public void updateKernelWakelocksLocked() {
@@ -14240,6 +14212,40 @@
mConstants.startObserving(context.getContentResolver());
registerUsbStateReceiver(context);
}
+ /**
+ * Initialize the measured energy stats data structures.
+ *
+ * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported
+ */
+ @GuardedBy("this")
+ public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) {
+ boolean supportedBucketMismatch = false;
+ mScreenStateAtLastEnergyMeasurement = mScreenState;
+
+ if (supportedEnergyBuckets == null) {
+ if (mGlobalMeasuredEnergyStats != null) {
+ // Measured energy buckets no longer supported, wipe out the existing data.
+ supportedBucketMismatch = true;
+ }
+ } else if (mGlobalMeasuredEnergyStats == null) {
+ mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ return;
+ } else {
+ for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i)
+ != supportedEnergyBuckets[i]) {
+ supportedBucketMismatch = true;
+ break;
+ }
+ }
+ }
+
+ if (supportedBucketMismatch) {
+ // Supported energy buckets changed since last boot.
+ // Existing data is no longer reliable.
+ resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
+ }
+ }
@VisibleForTesting
public final class Constants extends ContentObserver {
@@ -14919,7 +14925,11 @@
mNextMaxDailyDeadlineMs = in.readLong();
mBatteryTimeToFullSeconds = in.readLong();
- MeasuredEnergyStats.readSummaryFromParcel(mGlobalMeasuredEnergyStats, in);
+ /**
+ * WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled
+ * later when {@link #initMeasuredEnergyStatsLocked} is called.
+ */
+ mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in);
mStartCount++;
@@ -15417,7 +15427,7 @@
out.writeLong(mNextMaxDailyDeadlineMs);
out.writeLong(mBatteryTimeToFullSeconds);
- MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out);
+ MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -15742,7 +15752,7 @@
out.writeInt(0);
}
- MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out);
+ MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out, true);
final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
int NW = wakeStats.size();
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b644df4..b744a5d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -130,12 +130,16 @@
* Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
* parceling changes.
*/
- private void readSummaryFromParcel(Parcel in) {
+ private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final int bucket = in.readInt();
final long energyUJ = in.readLong();
- setValueIfSupported(bucket, energyUJ);
+ if (overwriteAvailability) {
+ mAccumulatedEnergiesMicroJoules[bucket] = energyUJ;
+ } else {
+ setValueIfSupported(bucket, energyUJ);
+ }
}
}
@@ -143,14 +147,15 @@
* Write to summary parcel.
* Note: Measured subsystem availability may be different when the summary parcel is read.
*/
- private void writeSummaryToParcel(Parcel out) {
+ private void writeSummaryToParcel(Parcel out, boolean skipZero) {
final int sizePos = out.dataPosition();
out.writeInt(0);
int size = 0;
// Write only the supported buckets with non-zero energy.
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
final long energy = mAccumulatedEnergiesMicroJoules[i];
- if (energy <= 0) continue;
+ if (energy < 0) continue;
+ if (energy == 0 && skipZero) continue;
out.writeInt(i);
out.writeLong(mAccumulatedEnergiesMicroJoules[i]);
@@ -197,16 +202,19 @@
}
/**
- * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and
- * ignore the parcelled data.
+ * Create a MeasuredEnergyStats object from a summary parcel.
+ *
+ * @return a new MeasuredEnergyStats object as described.
+ * Returns null if the parcel indicates there is no data to populate.
*/
- public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) {
+ public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
// Check if any MeasuredEnergyStats exists on the parcel
- if (in.readInt() == 0) return;
+ if (in.readInt() == 0) return null;
- // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data
- final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats();
- mes.readSummaryFromParcel(in);
+ final MeasuredEnergyStats stats =
+ new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]);
+ stats.readSummaryFromParcel(in, true);
+ return stats;
}
/**
@@ -227,12 +235,12 @@
if (template == null) {
// Nothing supported now. Create placeholder object just to consume the parcel data.
final MeasuredEnergyStats mes = new MeasuredEnergyStats();
- mes.readSummaryFromParcel(in);
+ mes.readSummaryFromParcel(in, false);
return null;
}
final MeasuredEnergyStats stats = createFromTemplate(template);
- stats.readSummaryFromParcel(in);
+ stats.readSummaryFromParcel(in, false);
if (stats.containsInterestingData()) {
return stats;
} else {
@@ -253,13 +261,13 @@
* Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
*/
public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
- Parcel dest) {
+ Parcel dest, boolean skipZero) {
if (stats == null) {
dest.writeInt(0);
return;
}
dest.writeInt(1);
- stats.writeSummaryToParcel(dest);
+ stats.writeSummaryToParcel(dest, skipZero);
}
/** Reset accumulated energy. */
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index fa046c6..d3c2d31 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -626,6 +626,7 @@
optional int32 target_uid = 1;
optional int64 duration_ms = 2;
optional string tag = 3;
+ optional int32 type = 4;
}
repeated PendingTempWhitelist pending_temp_whitelist = 26;
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
new file mode 100644
index 0000000..1ff88f7
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+
+import org.junit.Test;
+
+public class AppSearchPersonTest {
+
+ @Test
+ public void testBuildPersonAndGetValue() {
+ final String name = "name";
+ final String key = "key";
+ final String uri = "name:name";
+
+ final Person person = new AppSearchPerson.Builder(uri)
+ .setName(name)
+ .setKey(key)
+ .setIsBot(true)
+ .setIsImportant(false)
+ .build()
+ .toPerson();
+
+ assertThat(person.getName()).isEqualTo(name);
+ assertThat(person.getKey()).isEqualTo(key);
+ assertThat(person.getUri()).isEqualTo(uri);
+ assertThat(person.isBot()).isTrue();
+ assertThat(person.isImportant()).isFalse();
+ }
+}
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
new file mode 100644
index 0000000..da92e69
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.util.ArraySet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class AppSearchShortcutInfoTest {
+
+ @Test
+ public void testBuildShortcutAndGetValue() {
+ final String category =
+ "android.app.stubs.SHARE_SHORTCUT_CATEGORY";
+ final String id = "shareShortcut";
+ final String shortcutIconResName = "shortcut";
+ final ComponentName activity = new ComponentName("xxx", "s");
+ final Person person = new Person.Builder()
+ .setBot(false)
+ .setName("BubbleBot")
+ .setImportant(true)
+ .build();
+
+ final Set<String> categorySet = new ArraySet<>();
+ categorySet.add(category);
+ final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
+ final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id)
+ .setActivity(activity)
+ .setText(id)
+ .setIconResName(shortcutIconResName)
+ .setIntent(shortcutIntent)
+ .setPerson(person)
+ .setCategories(categorySet)
+ .setFlags(ShortcutInfo.FLAG_LONG_LIVED)
+ .build()
+ .toShortcutInfo();
+
+ assertThat(shortcut.getId()).isEqualTo(id);
+ assertThat(shortcut.getShortLabel()).isEqualTo(id);
+ assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName);
+ assertThat(shortcut.getIntent().toString()).isEqualTo(shortcut.toString());
+ assertThat(shortcut.getPersons().length).isEqualTo(1);
+ assertThat(shortcut.getPersons()[0]).isEqualTo(person);
+ assertThat(shortcut.getCategories()).isEqualTo(categorySet);
+ assertThat(shortcut.getFlags()).isEqualTo(ShortcutInfo.FLAG_LONG_LIVED);
+ assertThat(shortcut.getActivity()).isEqualTo(activity);
+ }
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f371a7f..f171ab4 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -35,6 +35,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.content.Context;
import android.os.Binder;
@@ -50,6 +51,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for {@link ViewRootImpl}
*
@@ -196,6 +200,26 @@
}
/**
+ * Ensure scroll capture request handles a ViewRootImpl with no view tree.
+ */
+ @Test
+ public void requestScrollCapture_withoutContentRoot() {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ @Override
+ public void onUnavailable() {
+ latch.countDown();
+ }
+ });
+ try {
+ if (latch.await(100, TimeUnit.MILLISECONDS)) {
+ return; // pass
+ }
+ } catch (InterruptedException e) { /* ignore */ }
+ fail("requestScrollCapture did not respond");
+ }
+
+ /**
* When window doesn't have focus, keys should be dropped.
*/
@Test
diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
index 5371a0f..ccd873d 100644
--- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
+++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
@@ -30,7 +30,6 @@
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.platform.test.annotations.Presubmit;
-import android.view.View;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -48,6 +47,7 @@
@Presubmit
public class AbsSeekBarTest {
+ public static final int PADDING = 10;
private Context mContext;
private AbsSeekBar mBar;
@@ -59,34 +59,42 @@
@Test
public void testExclusionForThumb_limitedTo48dp() {
- mBar.setPadding(10, 10, 10, 10);
+ mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
mBar.setThumb(newThumb(dpToPxSize(20)));
mBar.setMin(0);
mBar.setMax(100);
mBar.setProgress(50);
+
+ final int thumbOffset = mBar.getThumbOffset();
+
measureAndLayout(dpToPxSize(200), dpToPxSize(100));
List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
assertEquals("exclusion should be centered on thumb",
- center(mBar), center(exclusions.get(0)));
+ center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)),
+ center(exclusions.get(0)));
assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height());
assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width());
}
@Test
public void testExclusionForThumb_limitedToHeight() {
- mBar.setPadding(10, 10, 10, 10);
+ mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
mBar.setThumb(newThumb(dpToPxSize(20)));
mBar.setMin(0);
mBar.setMax(100);
mBar.setProgress(50);
+
+ final int thumbOffset = mBar.getThumbOffset();
+
measureAndLayout(dpToPxSize(200), dpToPxSize(32));
List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
assertEquals("exclusion should be centered on thumb",
- center(mBar), center(exclusions.get(0)));
+ center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)),
+ center(exclusions.get(0)));
assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height());
assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width());
}
@@ -95,7 +103,7 @@
public void testExclusionForThumb_passesThroughUserExclusions() {
mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4)));
- mBar.setPadding(10, 10, 10, 10);
+ mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
mBar.setThumb(newThumb(dpToPxSize(20)));
mBar.setMin(0);
mBar.setMax(100);
@@ -110,12 +118,37 @@
assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2));
}
+ @Test
+ public void testGrowRectTo_evenInitialDifference() {
+ doGrowRectTest(new Rect(0, 0, 0, 0), 10, new Rect(-5, -5, 5, 5));
+ }
+
+ @Test
+ public void testGrowRectTo_unevenInitialDifference() {
+ doGrowRectTest(new Rect(0, 0, 1, 1), 10, new Rect(-5, -5, 5, 5));
+ }
+
+ @Test
+ public void testGrowRectTo_unevenInitialDifference_unevenSize() {
+ doGrowRectTest(new Rect(0, 0, 0, 0), 9, new Rect(-5, -5, 4, 4));
+ }
+
+ public void doGrowRectTest(Rect in, int minimumSize, Rect expected) {
+ Rect result = new Rect(in);
+ mBar.growRectTo(result, minimumSize);
+
+ assertEquals("grown rect", expected, result);
+ assertEquals("grown rect center point", center(expected), center(result));
+ }
+
private Point center(Rect rect) {
return new Point(rect.centerX(), rect.centerY());
}
- private Point center(View view) {
- return center(new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+ private Rect offset(Rect rect, int dx, int dy) {
+ Rect result = new Rect(rect);
+ result.offset(dx, dy);
+ return result;
}
private ShapeDrawable newThumb(int size) {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 2ede751..c9c81ac 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -34,6 +34,8 @@
import org.junit.Test;
+import java.util.Arrays;
+
/**
* Test class for {@link MeasuredEnergyStats}.
*
@@ -114,7 +116,7 @@
}
@Test
- public void testReadWriteSummaryParcel() {
+ public void testCreateAndReadSummaryFromParcel() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
@@ -126,35 +128,21 @@
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
-
-
- final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true
- newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false
- MeasuredEnergyStats newStats = new MeasuredEnergyStats(newSupportedEnergyBuckets);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
parcel.setDataPosition(0);
- MeasuredEnergyStats.readSummaryFromParcel(newStats, parcel);
+ MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
- if (!newSupportedEnergyBuckets[i]) {
- assertFalse(newStats.isEnergyBucketSupported(i));
- assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
- } else if (!supportedEnergyBuckets[i]) {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
- } else {
- assertTrue(newStats.isEnergyBucketSupported(i));
- assertEquals(stats.getAccumulatedBucketEnergy(i),
- newStats.getAccumulatedBucketEnergy(i));
- }
+ assertEquals(stats.isEnergyBucketSupported(i),
+ newStats.isEnergyBucketSupported(i));
+ assertEquals(stats.getAccumulatedBucketEnergy(i),
+ newStats.getAccumulatedBucketEnergy(i));
}
parcel.recycle();
}
@Test
- public void testCreateAndReadSummaryFromParcel() {
+ public void testCreateAndReadSummaryFromParcel_existingTemplate() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
@@ -171,7 +159,7 @@
stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
final Parcel parcel = Parcel.obtain();
- MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+ MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
@@ -200,6 +188,55 @@
}
@Test
+ public void testCreateAndReadSummaryFromParcel_skipZero() {
+ final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
+ Arrays.fill(supportedEnergyBuckets, true);
+
+ final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
+ // Accumulate energy in one bucket, the rest should be zero
+ stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+
+ final Parcel includeZerosParcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
+ includeZerosParcel.setDataPosition(0);
+
+ MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
+ includeZerosParcel);
+
+ for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ if (i == ENERGY_BUCKET_SCREEN_ON) {
+ assertEquals(stats.isEnergyBucketSupported(i),
+ newStats.isEnergyBucketSupported(i));
+ assertEquals(stats.getAccumulatedBucketEnergy(i),
+ newStats.getAccumulatedBucketEnergy(i));
+ } else {
+ assertTrue(newStats.isEnergyBucketSupported(i));
+ assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+ }
+ }
+ includeZerosParcel.recycle();
+
+ final Parcel skipZerosParcel = Parcel.obtain();
+ MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
+ skipZerosParcel.setDataPosition(0);
+
+ newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
+
+ for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+ if (i == ENERGY_BUCKET_SCREEN_ON) {
+ assertEquals(stats.isEnergyBucketSupported(i),
+ newStats.isEnergyBucketSupported(i));
+ assertEquals(stats.getAccumulatedBucketEnergy(i),
+ newStats.getAccumulatedBucketEnergy(i));
+ } else {
+ assertFalse(newStats.isEnergyBucketSupported(i));
+ assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+ }
+ }
+ skipZerosParcel.recycle();
+ }
+
+ @Test
public void testUpdateBucket() {
final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java
new file mode 100644
index 0000000..14d6626
--- /dev/null
+++ b/keystore/java/android/security/AuthTokenUtils.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.Timestamp;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * @hide This Utils class provides method(s) for AuthToken conversion.
+ */
+public class AuthTokenUtils {
+
+ private AuthTokenUtils(){
+ }
+
+ /**
+ * Build a HardwareAuthToken from a byte array
+ * @param array byte array representing an auth token
+ * @return HardwareAuthToken representation of an auth token
+ */
+ public static @NonNull HardwareAuthToken toHardwareAuthToken(@NonNull byte[] array) {
+ final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+
+ // First byte is version, which does not exist in HardwareAuthToken anymore
+ // Next 8 bytes is the challenge.
+ hardwareAuthToken.challenge =
+ ByteBuffer.wrap(array, 1, 8).order(ByteOrder.nativeOrder()).getLong();
+
+ // Next 8 bytes is the userId
+ hardwareAuthToken.userId =
+ ByteBuffer.wrap(array, 9, 8).order(ByteOrder.nativeOrder()).getLong();
+
+ // Next 8 bytes is the authenticatorId.
+ hardwareAuthToken.authenticatorId =
+ ByteBuffer.wrap(array, 17, 8).order(ByteOrder.nativeOrder()).getLong();
+
+ // while the other fields are in machine byte order, authenticatorType and timestamp
+ // are in network byte order.
+ // Next 4 bytes is the authenticatorType.
+ hardwareAuthToken.authenticatorType =
+ ByteBuffer.wrap(array, 25, 4).order(ByteOrder.BIG_ENDIAN).getInt();
+ // Next 8 bytes is the timestamp.
+ final Timestamp timestamp = new Timestamp();
+ timestamp.milliSeconds =
+ ByteBuffer.wrap(array, 29, 8).order(ByteOrder.BIG_ENDIAN).getLong();
+ hardwareAuthToken.timestamp = timestamp;
+
+ // Last 32 bytes is the mac, 37:69
+ hardwareAuthToken.mac = new byte[32];
+ System.arraycopy(array, 37 /* srcPos */,
+ hardwareAuthToken.mac,
+ 0 /* destPos */,
+ 32 /* length */);
+
+ return hardwareAuthToken;
+ }
+}
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
new file mode 100644
index 0000000..1fde2b5
--- /dev/null
+++ b/keystore/java/android/security/Authorization.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import android.annotation.NonNull;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.authorization.IKeystoreAuthorization;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+/**
+ * @hide This is the client side for IKeystoreAuthorization AIDL.
+ * It shall only be used by biometric authentication providers and Gatekeeper.
+ */
+public class Authorization {
+ private static final String TAG = "KeystoreAuthorization";
+ private static IKeystoreAuthorization sIKeystoreAuthorization;
+
+ public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
+
+ public Authorization() {
+ sIKeystoreAuthorization = null;
+ }
+
+ private static synchronized IKeystoreAuthorization getService() {
+ if (sIKeystoreAuthorization == null) {
+ sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface(
+ ServiceManager.checkService("android.security.authorization"));
+ }
+ return sIKeystoreAuthorization;
+ }
+
+ /**
+ * Adds an auth token to keystore2.
+ *
+ * @param authToken created by Android authenticators.
+ * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
+ */
+ public int addAuthToken(@NonNull HardwareAuthToken authToken) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().addAuthToken(authToken);
+ return 0;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ } catch (ServiceSpecificException e) {
+ return e.errorCode;
+ }
+ }
+
+ /**
+ * Add an auth token to Keystore 2.0 in the legacy serialized auth token format.
+ * @param authToken
+ * @return 0 if successful or a {@code ResponseCode}.
+ */
+ public int addAuthToken(@NonNull byte[] authToken) {
+ return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken));
+ }
+
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index c70c986..4a67135 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -996,6 +996,7 @@
*/
public int addAuthToken(byte[] authToken) {
try {
+ new Authorization().addAuthToken(authToken);
return mBinder.addAuthToken(authToken);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 8d0e965..dd43139 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -252,7 +252,9 @@
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
// TODO: Synchronize show with the resize
onLocationChanged();
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ }
if (mListener != null) {
mListenerExecutor.execute(() -> {
@@ -279,8 +281,9 @@
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
- mTaskInfo.taskDescription = taskInfo.taskDescription;
- setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ if (taskInfo.taskDescription != null) {
+ setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+ }
}
@Override
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 6bf2e99..11a9086 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,12 +16,14 @@
#include "RecordingCanvas.h"
-#include "pipeline/skia/FunctorDrawable.h"
-#include "VectorDrawable.h"
+#include <GrRecordingContext.h>
+
+#include <experimental/type_traits>
#include "SkAndroidFrameworkUtils.h"
#include "SkCanvas.h"
#include "SkCanvasPriv.h"
+#include "SkColor.h"
#include "SkData.h"
#include "SkDrawShadowInfo.h"
#include "SkImage.h"
@@ -33,8 +35,8 @@
#include "SkRegion.h"
#include "SkTextBlob.h"
#include "SkVertices.h"
-
-#include <experimental/type_traits>
+#include "VectorDrawable.h"
+#include "pipeline/skia/FunctorDrawable.h"
namespace android {
namespace uirenderer {
@@ -500,7 +502,68 @@
// SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw.
// SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke
// onSnapGpuDrawHandler.
- void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); }
+private:
+ // Unfortunately WebView does not have complex clip information serialized, and we only perform
+ // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the
+ // canvas clip is complex.
+ static bool needsCompositedLayer(SkCanvas* c) {
+ if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+ return false;
+ }
+ SkRegion clipRegion;
+ // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if
+ // the clip is more complex than a rectangle.
+ c->temporary_internal_getRgnClip(&clipRegion);
+ return clipRegion.isComplex();
+ }
+
+ mutable SkImageInfo mLayerImageInfo;
+ mutable sk_sp<SkSurface> mLayerSurface = nullptr;
+
+public:
+ void draw(SkCanvas* c, const SkMatrix&) const {
+ if (needsCompositedLayer(c)) {
+ // What we do now is create an offscreen surface, sized by the clip bounds.
+ // We won't apply a clip while drawing - clipping will be performed when compositing the
+ // surface back onto the original canvas. Note also that we're not using saveLayer
+ // because the webview functor still doesn't respect the canvas clip stack.
+ const SkIRect deviceBounds = c->getDeviceClipBounds();
+ if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) {
+ GrRecordingContext* directContext = c->recordingContext();
+ mLayerImageInfo =
+ c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height());
+ mLayerSurface = SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes,
+ mLayerImageInfo, 0,
+ kTopLeft_GrSurfaceOrigin, nullptr);
+ }
+
+ SkCanvas* layerCanvas = mLayerSurface->getCanvas();
+
+ SkAutoCanvasRestore(layerCanvas, true);
+ layerCanvas->clear(SK_ColorTRANSPARENT);
+
+ // Preserve the transform from the original canvas, but now the clip rectangle is
+ // anchored at the origin so we need to transform the clipped content to the origin.
+ SkM44 mat4(c->getLocalToDevice());
+ mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop);
+ layerCanvas->concat(mat4);
+ layerCanvas->drawDrawable(drawable.get());
+
+ SkAutoCanvasRestore acr(c, true);
+
+ // Temporarily use an identity transform, because this is just blitting to the parent
+ // canvas with an offset.
+ SkMatrix invertedMatrix;
+ if (!c->getTotalMatrix().invert(&invertedMatrix)) {
+ ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw");
+ return;
+ }
+ c->concat(invertedMatrix);
+ mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+ } else {
+ c->drawDrawable(drawable.get());
+ }
+ }
};
}
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index 52d952c..e55678d9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -20,6 +20,7 @@
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
/**
* Interface to the playback manager service.
@@ -31,4 +32,5 @@
void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
+ void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java
index 63a50ae..f48ffe7 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -73,6 +73,18 @@
}
/**
+ * Reports track change event.
+ * @hide
+ */
+ public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) {
+ try {
+ mService.reportTrackChangeEvent(sessionId, event, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a playback session.
*/
public PlaybackSession createSession() {
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 061e665..0a77516 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -71,6 +71,13 @@
mManager.reportPlaybackStateEvent(mId, event);
}
+ /**
+ * Reports track change event.
+ */
+ public void reportTrackChangeEvent(TrackChangeEvent event) {
+ mManager.reportTrackChangeEvent(mId, event);
+ }
+
public @NonNull String getId() {
return mId;
}
diff --git a/media/java/android/media/metrics/TrackChangeEvent.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl
new file mode 100644
index 0000000..8fbe4a9
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+parcelable TrackChangeEvent;
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
new file mode 100644
index 0000000..fff0e36
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.metrics;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Playback track change event.
+ * @hide
+ */
+public final class TrackChangeEvent implements Parcelable {
+ public static final int TRACK_STATE_OFF = 0;
+ public static final int TRACK_STATE_ON = 1;
+
+ public static final int TRACK_CHANGE_REASON_UNKNOWN = 0;
+ public static final int TRACK_CHANGE_REASON_OTHER = 1;
+ public static final int TRACK_CHANGE_REASON_INITIAL = 2;
+ public static final int TRACK_CHANGE_REASON_MANUAL = 3;
+ public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4;
+
+ public static final int TRACK_TYPE_AUDIO = 0;
+ public static final int TRACK_TYPE_VIDEO = 1;
+ public static final int TRACK_TYPE_TEXT = 2;
+
+ private final int mState;
+ private final int mReason;
+ private final @Nullable String mContainerMimeType;
+ private final @Nullable String mSampleMimeType;
+ private final @Nullable String mCodecName;
+ private final int mBitrate;
+ private final long mTimeSincePlaybackCreatedMillis;
+ private final int mType;
+ private final @Nullable String mLanguage;
+ private final @Nullable String mLanguageRegion;
+ private final int mChannelCount;
+ private final int mSampleRate;
+ private final int mWidth;
+ private final int mHeight;
+
+
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_STATE_", value = {
+ TRACK_STATE_OFF,
+ TRACK_STATE_ON
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackState {}
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_CHANGE_REASON_", value = {
+ TRACK_CHANGE_REASON_UNKNOWN,
+ TRACK_CHANGE_REASON_OTHER,
+ TRACK_CHANGE_REASON_INITIAL,
+ TRACK_CHANGE_REASON_MANUAL,
+ TRACK_CHANGE_REASON_ADAPTIVE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackChangeReason {}
+
+ /** @hide */
+ @IntDef(prefix = "TRACK_TYPE_", value = {
+ TRACK_TYPE_AUDIO,
+ TRACK_TYPE_VIDEO,
+ TRACK_TYPE_TEXT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TrackType {}
+
+ public TrackChangeEvent(
+ int state,
+ int reason,
+ @Nullable String containerMimeType,
+ @Nullable String sampleMimeType,
+ @Nullable String codecName,
+ int bitrate,
+ long timeSincePlaybackCreatedMillis,
+ int type,
+ @Nullable String language,
+ @Nullable String languageRegion,
+ int channelCount,
+ int sampleRate,
+ int width,
+ int height) {
+ this.mState = state;
+ this.mReason = reason;
+ this.mContainerMimeType = containerMimeType;
+ this.mSampleMimeType = sampleMimeType;
+ this.mCodecName = codecName;
+ this.mBitrate = bitrate;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mType = type;
+ this.mLanguage = language;
+ this.mLanguageRegion = languageRegion;
+ this.mChannelCount = channelCount;
+ this.mSampleRate = sampleRate;
+ this.mWidth = width;
+ this.mHeight = height;
+ }
+
+ @TrackState
+ public int getTrackState() {
+ return mState;
+ }
+
+ @TrackChangeReason
+ public int getTrackChangeReason() {
+ return mReason;
+ }
+
+ public @Nullable String getContainerMimeType() {
+ return mContainerMimeType;
+ }
+
+ public @Nullable String getSampleMimeType() {
+ return mSampleMimeType;
+ }
+
+ public @Nullable String getCodecName() {
+ return mCodecName;
+ }
+
+ public int getBitrate() {
+ return mBitrate;
+ }
+
+ public long getTimeSincePlaybackCreatedMillis() {
+ return mTimeSincePlaybackCreatedMillis;
+ }
+
+ @TrackType
+ public int getTrackType() {
+ return mType;
+ }
+
+ public @Nullable String getLanguage() {
+ return mLanguage;
+ }
+
+ public @Nullable String getLanguageRegion() {
+ return mLanguageRegion;
+ }
+
+ public int getChannelCount() {
+ return mChannelCount;
+ }
+
+ public int getSampleRate() {
+ return mSampleRate;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ int flg = 0;
+ if (mContainerMimeType != null) flg |= 0x4;
+ if (mSampleMimeType != null) flg |= 0x8;
+ if (mCodecName != null) flg |= 0x10;
+ if (mLanguage != null) flg |= 0x100;
+ if (mLanguageRegion != null) flg |= 0x200;
+ dest.writeInt(flg);
+ dest.writeInt(mState);
+ dest.writeInt(mReason);
+ if (mContainerMimeType != null) dest.writeString(mContainerMimeType);
+ if (mSampleMimeType != null) dest.writeString(mSampleMimeType);
+ if (mCodecName != null) dest.writeString(mCodecName);
+ dest.writeInt(mBitrate);
+ dest.writeLong(mTimeSincePlaybackCreatedMillis);
+ dest.writeInt(mType);
+ if (mLanguage != null) dest.writeString(mLanguage);
+ if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
+ dest.writeInt(mChannelCount);
+ dest.writeInt(mSampleRate);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ /* package-private */ TrackChangeEvent(@NonNull Parcel in) {
+ int flg = in.readInt();
+ int state = in.readInt();
+ int reason = in.readInt();
+ String containerMimeType = (flg & 0x4) == 0 ? null : in.readString();
+ String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString();
+ String codecName = (flg & 0x10) == 0 ? null : in.readString();
+ int bitrate = in.readInt();
+ long timeSincePlaybackCreatedMillis = in.readLong();
+ int type = in.readInt();
+ String language = (flg & 0x100) == 0 ? null : in.readString();
+ String languageRegion = (flg & 0x200) == 0 ? null : in.readString();
+ int channelCount = in.readInt();
+ int sampleRate = in.readInt();
+ int width = in.readInt();
+ int height = in.readInt();
+
+ this.mState = state;
+ this.mReason = reason;
+ this.mContainerMimeType = containerMimeType;
+ this.mSampleMimeType = sampleMimeType;
+ this.mCodecName = codecName;
+ this.mBitrate = bitrate;
+ this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+ this.mType = type;
+ this.mLanguage = language;
+ this.mLanguageRegion = languageRegion;
+ this.mChannelCount = channelCount;
+ this.mSampleRate = sampleRate;
+ this.mWidth = width;
+ this.mHeight = height;
+ }
+
+ public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
+ new Parcelable.Creator<TrackChangeEvent>() {
+ @Override
+ public TrackChangeEvent[] newArray(int size) {
+ return new TrackChangeEvent[size];
+ }
+
+ @Override
+ public TrackChangeEvent createFromParcel(@NonNull Parcel in) {
+ return new TrackChangeEvent(in);
+ }
+ };
+
+
+
+ // Code below generated by codegen v1.0.22.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+ @Override
+ public String toString() {
+ return "TrackChangeEvent { " +
+ "state = " + mState + ", " +
+ "reason = " + mReason + ", " +
+ "containerMimeType = " + mContainerMimeType + ", " +
+ "sampleMimeType = " + mSampleMimeType + ", " +
+ "codecName = " + mCodecName + ", " +
+ "bitrate = " + mBitrate + ", " +
+ "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " +
+ "type = " + mType + ", " +
+ "language = " + mLanguage + ", " +
+ "languageRegion = " + mLanguageRegion + ", " +
+ "channelCount = " + mChannelCount + ", " +
+ "sampleRate = " + mSampleRate + ", " +
+ "width = " + mWidth + ", " +
+ "height = " + mHeight +
+ " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TrackChangeEvent that = (TrackChangeEvent) o;
+ return mState == that.mState
+ && mReason == that.mReason
+ && Objects.equals(mContainerMimeType, that.mContainerMimeType)
+ && Objects.equals(mSampleMimeType, that.mSampleMimeType)
+ && Objects.equals(mCodecName, that.mCodecName)
+ && mBitrate == that.mBitrate
+ && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis
+ && mType == that.mType
+ && Objects.equals(mLanguage, that.mLanguage)
+ && Objects.equals(mLanguageRegion, that.mLanguageRegion)
+ && mChannelCount == that.mChannelCount
+ && mSampleRate == that.mSampleRate
+ && mWidth == that.mWidth
+ && mHeight == that.mHeight;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
+ mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion,
+ mChannelCount, mSampleRate, mWidth, mHeight);
+ }
+
+ /**
+ * A builder for {@link TrackChangeEvent}
+ */
+ public static final class Builder {
+ // TODO: check track type for the setters.
+ private int mState;
+ private int mReason;
+ private @Nullable String mContainerMimeType;
+ private @Nullable String mSampleMimeType;
+ private @Nullable String mCodecName;
+ private int mBitrate;
+ private long mTimeSincePlaybackCreatedMillis;
+ private int mType;
+ private @Nullable String mLanguage;
+ private @Nullable String mLanguageRegion;
+ private int mChannelCount;
+ private int mSampleRate;
+ private int mWidth;
+ private int mHeight;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @hide
+ */
+ public Builder(int type) {
+ mType = type;
+ }
+
+ public @NonNull Builder setTrackState(@TrackState int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mState = value;
+ return this;
+ }
+
+ public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mReason = value;
+ return this;
+ }
+
+ public @NonNull Builder setContainerMimeType(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mContainerMimeType = value;
+ return this;
+ }
+
+ public @NonNull Builder setSampleMimeType(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mSampleMimeType = value;
+ return this;
+ }
+
+ public @NonNull Builder setCodecName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mCodecName = value;
+ return this;
+ }
+
+ public @NonNull Builder setBitrate(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mBitrate = value;
+ return this;
+ }
+
+ public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40;
+ mTimeSincePlaybackCreatedMillis = value;
+ return this;
+ }
+
+ public @NonNull Builder setTrackType(@TrackType int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mType = value;
+ return this;
+ }
+
+ public @NonNull Builder setLanguage(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x100;
+ mLanguage = value;
+ return this;
+ }
+
+ public @NonNull Builder setLanguageRegion(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x200;
+ mLanguageRegion = value;
+ return this;
+ }
+
+ public @NonNull Builder setChannelCount(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x400;
+ mChannelCount = value;
+ return this;
+ }
+
+ public @NonNull Builder setSampleRate(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x800;
+ mSampleRate = value;
+ return this;
+ }
+
+ public @NonNull Builder setWidth(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1000;
+ mWidth = value;
+ return this;
+ }
+
+ public @NonNull Builder setHeight(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2000;
+ mHeight = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull TrackChangeEvent build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4000; // Mark builder used
+
+ TrackChangeEvent o = new TrackChangeEvent(
+ mState,
+ mReason,
+ mContainerMimeType,
+ mSampleMimeType,
+ mCodecName,
+ mBitrate,
+ mTimeSincePlaybackCreatedMillis,
+ mType,
+ mLanguage,
+ mLanguageRegion,
+ mChannelCount,
+ mSampleRate,
+ mWidth,
+ mHeight);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4000) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index d278c59..86aa214 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -83,7 +83,7 @@
return value == null || value.length() < MAX_LENGTH;
}
});
- VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
+ VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f));
VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.DISPLAY_COLOR_MODE,
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 672d2f6..c4cf440 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -73,7 +73,7 @@
<color name="media_divider">#85ffffff</color>
<!-- Biometric dialog colors -->
- <color name="biometric_dialog_gray">#ff888888</color>
+ <color name="biometric_dialog_gray">#ffcccccc</color>
<color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal -->
<color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index e4f6d6c..9b09cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -160,12 +160,12 @@
@Override
protected void handleResetAfterError() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
protected void handleResetAfterHelp() {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
@Override
@@ -185,7 +185,7 @@
if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
(newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
- resetErrorView(mContext, mIndicatorView);
+ resetErrorView();
}
// Do this last since the state variable gets updated.
@@ -204,9 +204,8 @@
super.onAuthenticationFailed(failureReason);
}
- static void resetErrorView(Context context, TextView textView) {
- textView.setTextColor(context.getResources().getColor(
- R.color.biometric_dialog_gray, context.getTheme()));
- textView.setVisibility(View.INVISIBLE);
+ private void resetErrorView() {
+ mIndicatorView.setTextColor(mTextColorHint);
+ mIndicatorView.setVisibility(View.INVISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
index 176e9e6..45ee4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
@@ -78,7 +78,7 @@
private void showTouchSensorString() {
mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
- mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
+ mIndicatorView.setTextColor(mTextColorHint);
}
private void updateIcon(int lastState, int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index c748ab2..18206ef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -83,7 +83,7 @@
* Authenticated, dialog animating away soon.
*/
protected static final int STATE_AUTHENTICATED = 6;
-
+
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP,
STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
@@ -168,8 +168,8 @@
private final Injector mInjector;
private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
- private final int mTextColorError;
- private final int mTextColorHint;
+ protected final int mTextColorError;
+ protected final int mTextColorHint;
private AuthPanelController mPanelController;
private PromptInfo mPromptInfo;
@@ -183,7 +183,7 @@
private TextView mDescriptionView;
private View mIconHolderView;
protected ImageView mIconView;
- @VisibleForTesting protected TextView mIndicatorView;
+ protected TextView mIndicatorView;
// Negative button position, exclusively for the app-specified behavior
@VisibleForTesting Button mNegativeButton;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index bba8579..e9207f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,7 +29,6 @@
import com.android.systemui.qs.TouchAnimator.Builder;
import com.android.systemui.qs.TouchAnimator.Listener;
import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -50,9 +49,7 @@
private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
public static final float EXPANDED_TILE_DELAY = .86f;
- private static final long QQS_FADE_IN_DURATION = 200L;
- // Fade out faster than fade in to finish before QQS hides.
- private static final long QQS_FADE_OUT_DURATION = 50L;
+
private final ArrayList<View> mAllViews = new ArrayList<>();
/**
@@ -80,7 +77,6 @@
private TouchAnimator mBrightnessAnimator;
private boolean mNeedsAnimatorUpdate = false;
- private boolean mToShowing;
private boolean mOnKeyguard;
private boolean mAllowFancy;
@@ -137,18 +133,6 @@
}
}
- void startAlphaAnimation(boolean show) {
- if (show == mToShowing) {
- return;
- }
- mToShowing = show;
- if (show) {
- CrossFadeHelper.fadeIn(mQs.getView(), QQS_FADE_IN_DURATION, 0 /* delay */);
- } else {
- CrossFadeHelper.fadeOut(mQs.getView(), QQS_FADE_OUT_DURATION, 0 /* delay */,
- null /* endRunnable */);
- }
- }
/**
* Sets whether or not the keyguard is currently being shown with a collapsed header.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 562ac64..16e9590 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -91,11 +91,6 @@
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
}
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
void onMediaVisibilityChanged(boolean qsVisible) {
mAnimateBottomOnNextLayout = qsVisible;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 9a0827d..dbdd04a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -186,7 +186,7 @@
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
if (sizeChanged) {
- setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
+ setQsExpansion(mLastQSExpansion, mLastQSExpansion);
}
});
}
@@ -391,9 +391,6 @@
@Override
public void setQsExpansion(float expansion, float headerTranslation) {
if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
- if (mQSAnimator != null) {
- mQSAnimator.startAlphaAnimation(headerTranslation == 0 /* show */);
- }
mContainer.setExpansion(expansion);
final float translationScaleY = expansion - 1;
boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 3357be8..fe01f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.os.Build;
import android.os.PowerManager;
import androidx.test.filters.SmallTest;
@@ -85,4 +86,14 @@
assertTrue(ran[0]);
assertFalse(mInner.isHeld());
}
+
+ @Test
+ public void prodBuild_wakeLock_releaseWithoutAcquire_noThrow() {
+ if (Build.IS_ENG) {
+ return;
+ }
+
+ // shouldn't throw an exception on production builds
+ mWakeLock.release(WHY);
+ }
}
\ No newline at end of file
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index c6f42f7..a31cfae 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -1,4 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
-qasid@google.com
+ryanlwlin@google.com
diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java
new file mode 100644
index 0000000..9c908c3
--- /dev/null
+++ b/services/core/java/android/power/PowerStatsInternal.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.power;
+
+import android.hardware.power.stats.EnergyConsumerId;
+import android.hardware.power.stats.EnergyConsumerResult;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Power stats local system service interface.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class PowerStatsInternal {
+ /**
+ * Returns a CompletableFuture that will get an {@link EnergyConsumerResult} array for the
+ * available requested energy consumers (power models).
+ *
+ * @param energyConsumerIds Array of {@link EnergyConsumerId} for which energy consumed is being
+ * requested.
+ *
+ * @return A Future containing a list of {@link EnergyConsumerResult} objects containing energy
+ * consumer results for all listed {@link EnergyConsumerId}.
+ */
+ public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+ @EnergyConsumerId int[] energyConsumerIds);
+}
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index a6d9bf8..f04af8b 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -73,7 +73,6 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.SortedSet;
@@ -446,7 +445,10 @@
// from an in-memory buffer, or another file on disk; if we buffered
// we'd lose out on sendfile() optimizations
if (forceCompress) {
- FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd)));
+ final GZIPOutputStream gzipOutputStream =
+ new GZIPOutputStream(new FileOutputStream(fd));
+ FileUtils.copy(in, gzipOutputStream);
+ gzipOutputStream.finish();
} else {
FileUtils.copy(in, new FileOutputStream(fd));
}
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index dfcc325..b14ce1c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -5,7 +5,7 @@
per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
# Zram writeback
-per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com
+per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
# Userspace reboot
per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com
@@ -30,6 +30,7 @@
per-file MmsServiceBroker.java = file:/telephony/OWNERS
per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS
+per-file PinnerService.java = file:/apct-tests/perftests/OWNERS
per-file TelephonyRegistry.java = file:/telephony/OWNERS
per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS
per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index a3bcbbe..871de0d 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -43,6 +43,8 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -61,7 +63,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.SystemService.TargetUser;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.DexFile;
@@ -70,6 +71,7 @@
import java.io.Closeable;
import java.io.DataInputStream;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
@@ -100,12 +102,6 @@
private static final int KEY_HOME = 1;
private static final int KEY_ASSISTANT = 2;
- // Pin the camera application. Default to the system property only if the experiment phenotype
- // property is not set.
- private static boolean PROP_PIN_CAMERA =
- DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
- "pin_camera",
- SystemProperties.getBoolean("pinner.pin_camera", false));
// Pin using pinlist.meta when pinning apps.
private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
"pinner.use_pinlist", true);
@@ -150,7 +146,13 @@
/**
* A set of {@link AppKey} that are configured to be pinned.
*/
- private final ArraySet<Integer> mPinKeys = new ArraySet<>();
+ @GuardedBy("this")
+ private ArraySet<Integer> mPinKeys;
+
+ // Resource-configured pinner flags;
+ private final boolean mConfiguredToPinCamera;
+ private final boolean mConfiguredToPinHome;
+ private final boolean mConfiguredToPinAssistant;
private BinderService mBinderService;
private PinnerHandler mPinnerHandler = null;
@@ -173,25 +175,13 @@
super(context);
mContext = context;
- boolean shouldPinCamera = context.getResources().getBoolean(
+ mConfiguredToPinCamera = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerCameraApp);
- boolean shouldPinHome = context.getResources().getBoolean(
+ mConfiguredToPinHome = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerHomeApp);
- boolean shouldPinAssistant = context.getResources().getBoolean(
+ mConfiguredToPinAssistant = context.getResources().getBoolean(
com.android.internal.R.bool.config_pinnerAssistantApp);
- if (shouldPinCamera) {
- if (PROP_PIN_CAMERA) {
- mPinKeys.add(KEY_CAMERA);
- } else if (DEBUG) {
- Slog.i(TAG, "Pinner - skip pinning camera app");
- }
- }
- if (shouldPinHome) {
- mPinKeys.add(KEY_HOME);
- }
- if (shouldPinAssistant) {
- mPinKeys.add(KEY_ASSISTANT);
- }
+ mPinKeys = createPinKeys();
mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
@@ -259,9 +249,10 @@
* The other files pinned in onStart will not need to be updated.
*/
public void update(ArraySet<String> updatedPackages, boolean force) {
+ ArraySet<Integer> pinKeys = getPinKeys();
int currentUser = ActivityManager.getCurrentUser();
- for (int i = mPinKeys.size() - 1; i >= 0; i--) {
- int key = mPinKeys.valueAt(i);
+ for (int i = pinKeys.size() - 1; i >= 0; i--) {
+ int key = pinKeys.valueAt(i);
ApplicationInfo info = getInfoForKey(key, currentUser);
if (info != null && updatedPackages.contains(info.packageName)) {
Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
@@ -385,6 +376,14 @@
}
}
+ private void unpinApps() {
+ ArraySet<Integer> pinKeys = getPinKeys();
+ for (int i = pinKeys.size() - 1; i >= 0; i--) {
+ int key = pinKeys.valueAt(i);
+ unpinApp(key);
+ }
+ }
+
private void unpinApp(@AppKey int key) {
ArrayList<PinnedFile> pinnedAppFiles;
synchronized (this) {
@@ -490,9 +489,79 @@
userHandle));
}
+ private void sendPinAppsWithUpdatedKeysMessage(int userHandle) {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys,
+ this, userHandle));
+ }
+ private void sendUnpinAppsMessage() {
+ mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this));
+ }
+
+ private ArraySet<Integer> createPinKeys() {
+ ArraySet<Integer> pinKeys = new ArraySet<>();
+ // Pin the camera application. Default to the system property only if the experiment
+ // phenotype property is not set.
+ boolean shouldPinCamera = mConfiguredToPinCamera
+ && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ "pin_camera",
+ SystemProperties.getBoolean("pinner.pin_camera", false));
+ if (shouldPinCamera) {
+ pinKeys.add(KEY_CAMERA);
+ } else if (DEBUG) {
+ Slog.i(TAG, "Pinner - skip pinning camera app");
+ }
+
+ if (mConfiguredToPinHome) {
+ pinKeys.add(KEY_HOME);
+ }
+ if (mConfiguredToPinAssistant) {
+ pinKeys.add(KEY_ASSISTANT);
+ }
+
+ return pinKeys;
+ }
+
+ private static boolean shouldPinSplitApks() {
+ // For now this is disabled by default bcause the pinlist support for split APKs are
+ // missing in the toolchain. This flag should be removed once it is ready. b/174697187.
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ "pin_split_apks", false);
+ }
+
+ private synchronized ArraySet<Integer> getPinKeys() {
+ return mPinKeys;
+ }
+
private void pinApps(int userHandle) {
- for (int i = mPinKeys.size() - 1; i >= 0; i--) {
- int key = mPinKeys.valueAt(i);
+ pinAppsInternal(userHandle, false);
+ }
+
+ private void pinAppsWithUpdatedKeys(int userHandle) {
+ pinAppsInternal(userHandle, true);
+ }
+
+ /**
+ * @param updateKeys True if the pinned app list has to be updated. This is true only when
+ * "pinner repin" shell command is requested.
+ */
+ private void pinAppsInternal(int userHandle, boolean updateKeys) {
+ if (updateKeys) {
+ ArraySet<Integer> newKeys = createPinKeys();
+ synchronized (this) {
+ // This code path demands preceding unpinApps() call.
+ if (!mPinnedApps.isEmpty()) {
+ Slog.e(TAG, "Attempted to update a list of apps, "
+ + "but apps were already pinned. Skipping.");
+ return;
+ }
+
+ mPinKeys = newKeys;
+ }
+ }
+
+ ArraySet<Integer> currentPinKeys = getPinKeys();
+ for (int i = currentPinKeys.size() - 1; i >= 0; i--) {
+ int key = currentPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
}
@@ -610,19 +679,40 @@
mPinnedApps.put(key, pinnedApp);
}
+
// pin APK
- int pinSizeLimit = getSizeLimitForKey(key);
- String apk = appInfo.sourceDir;
- PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
- if (pf == null) {
- Slog.e(TAG, "Failed to pin " + apk);
- return;
+ final int pinSizeLimit = getSizeLimitForKey(key);
+ List<String> apks = new ArrayList<>();
+ apks.add(appInfo.sourceDir);
+
+ if (shouldPinSplitApks() && appInfo.splitSourceDirs != null) {
+ for (String splitApk : appInfo.splitSourceDirs) {
+ apks.add(splitApk);
+ }
}
- if (DEBUG) {
- Slog.i(TAG, "Pinned " + pf.fileName);
- }
- synchronized (this) {
- pinnedApp.mFiles.add(pf);
+
+ int apkPinSizeLimit = pinSizeLimit;
+ for (String apk: apks) {
+ if (apkPinSizeLimit <= 0) {
+ Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk);
+ // Continue instead of break to print all skipped APK names.
+ continue;
+ }
+
+ PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin " + apk);
+ continue;
+ }
+
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned " + pf.fileName);
+ }
+ synchronized (this) {
+ pinnedApp.mFiles.add(pf);
+ }
+
+ apkPinSizeLimit -= pf.bytesPinned;
}
// determine the ABI from either ApplicationInfo or Build
@@ -641,7 +731,7 @@
//not pinning the oat/odex is not a fatal error
for (String file : files) {
- pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
+ PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
if (pf != null) {
synchronized (this) {
if (PROP_PIN_ODEX) {
@@ -981,6 +1071,42 @@
}
}
}
+
+ private void repin() {
+ sendUnpinAppsMessage();
+ // TODO(morrita): Consider supporting non-system user.
+ sendPinAppsWithUpdatedKeysMessage(UserHandle.USER_SYSTEM);
+ }
+
+ private void printError(FileDescriptor out, String message) {
+ PrintWriter writer = new PrintWriter(new FileOutputStream(out));
+ writer.println(message);
+ writer.flush();
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ if (args.length < 1) {
+ printError(out, "Command is not given.");
+ resultReceiver.send(-1, null);
+ return;
+ }
+
+ String command = args[0];
+ switch (command) {
+ case "repin":
+ repin();
+ break;
+ default:
+ printError(out, String.format(
+ "Unknown pinner command: %s. Supported commands: repin", command));
+ resultReceiver.send(-1, null);
+ return;
+ }
+
+ resultReceiver.send(0, null);
+ }
}
private static final class PinnedFile implements AutoCloseable {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index c191a78..2fdc796 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.vcn.IVcnManagementService;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.os.Binder;
import android.os.Handler;
@@ -495,4 +496,20 @@
return Collections.unmodifiableMap(mVcns);
}
}
+
+ /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
+ @Override
+ public void addVcnUnderlyingNetworkPolicyListener(
+ IVcnUnderlyingNetworkPolicyListener listener) {
+ // TODO(b/175739863): implement policy listener registration
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
+ @Override
+ public void removeVcnUnderlyingNetworkPolicyListener(
+ IVcnUnderlyingNetworkPolicyListener listener) {
+ // TODO(b/175739863): implement policy listener unregistration
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0291ce4c..1f48aeb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,6 +32,7 @@
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.AppOpsManager.OP_NONE;
+import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -1085,11 +1086,13 @@
final int targetUid;
final long duration;
final String tag;
+ final int type;
- PendingTempWhitelist(int _targetUid, long _duration, String _tag) {
+ PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) {
targetUid = _targetUid;
duration = _duration;
tag = _tag;
+ type = _type;
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1097,6 +1100,7 @@
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
+ proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type);
proto.end(token);
}
}
@@ -5526,8 +5530,7 @@
}
boolean isWhitelistedForFgsStartLocked(int uid) {
- final int appId = UserHandle.getAppId(uid);
- return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0
+ return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0
|| mFgsStartTempAllowList.isAllowed(uid);
}
@@ -9298,6 +9301,8 @@
TimeUtils.formatDuration(ptw.duration, pw);
pw.print(" ");
pw.println(ptw.tag);
+ pw.print(" ");
+ pw.print(ptw.type);
}
}
}
@@ -15359,11 +15364,12 @@
*/
@GuardedBy("this")
void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) {
- mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
+ mPendingTempWhitelist.put(targetUid,
+ new PendingTempWhitelist(targetUid, duration, tag, type));
setUidTempWhitelistStateLocked(targetUid, true);
mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
- if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
mFgsStartTempAllowList.add(targetUid, duration);
}
}
@@ -15389,7 +15395,7 @@
for (int i = 0; i < N; i++) {
PendingTempWhitelist ptw = list[i];
mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
- ptw.duration, true, ptw.tag);
+ ptw.duration, ptw.type, true, ptw.tag);
}
}
@@ -15406,8 +15412,8 @@
}
@GuardedBy("this")
- final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
- mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist);
+ final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+ mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist);
}
@GuardedBy("this")
@@ -16009,10 +16015,16 @@
}
@Override
- public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) {
+ public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
+ long durationMs, @BroadcastOptions.TempAllowListType int type) {
synchronized (ActivityManagerService.this) {
mDeviceIdleTempWhitelist = appids;
- setAppIdTempWhitelistStateLocked(changingAppId, adding);
+ if (adding) {
+ if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+ mFgsStartTempAllowList.add(changingUid, durationMs);
+ }
+ }
+ setAppIdTempWhitelistStateLocked(changingUid, adding);
}
}
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 6fe934e..ada7eea 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -17,21 +17,24 @@
import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
+import android.hardware.power.stats.EnergyConsumerId;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.OutcomeReceiver;
import android.os.Parcelable;
import android.os.Process;
-import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
import android.os.ThreadLocalWorkSource;
import android.os.connectivity.WifiActivityEnergyInfo;
+import android.power.PowerStatsInternal;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
@@ -41,8 +44,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.power.MeasuredEnergyArray;
+import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
import libcore.util.EmptyArray;
@@ -91,6 +96,8 @@
});
private final Context mContext;
+
+ @GuardedBy("mStats")
private final BatteryStatsImpl mStats;
@GuardedBy("this")
@@ -123,6 +130,7 @@
@GuardedBy("this")
private Future<?> mBatteryLevelSync;
+ // If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first.
private final Object mWorkerLock = new Object();
@GuardedBy("mWorkerLock")
@@ -131,6 +139,9 @@
@GuardedBy("mWorkerLock")
private TelephonyManager mTelephony = null;
+ @GuardedBy("mWorkerLock")
+ private PowerStatsInternal mPowerStatsInternal = null;
+
// WiFi keeps an accumulated total of stats, unlike Bluetooth.
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mWorkerLock")
@@ -139,7 +150,7 @@
/** Snapshot of measured energies, or null if no measured energies are supported. */
@GuardedBy("mWorkerLock")
- private final @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot;
+ private @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot = null;
/**
* Timestamp at which all external stats were last collected in
@@ -148,13 +159,27 @@
@GuardedBy("this")
private long mLastCollectionTimeStamp;
- BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats,
- @Nullable MeasuredEnergyArray initialMeasuredEnergies) {
+ BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
mContext = context;
mStats = stats;
+ }
- mMeasuredEnergySnapshot = initialMeasuredEnergies == null ?
- null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+ public void systemServicesReady() {
+ final WifiManager wm = mContext.getSystemService(WifiManager.class);
+ final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ final PowerStatsInternal psi = LocalServices.getService(PowerStatsInternal.class);
+ final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(psi);
+ final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialMeasuredEnergies);
+ synchronized (mWorkerLock) {
+ mWifiManager = wm;
+ mTelephony = tm;
+ mPowerStatsInternal = psi;
+ mMeasuredEnergySnapshot = initialMeasuredEnergies == null
+ ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+ synchronized (mStats) {
+ mStats.initMeasuredEnergyStatsLocked(supportedBuckets);
+ }
+ }
}
@Override
@@ -433,14 +458,6 @@
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
// We were asked to fetch WiFi data.
- if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) {
- // this code is reached very early in the boot process, before Wifi Service has
- // been registered. Check that ServiceManager.getService() returns a non null
- // value before calling mContext.getSystemService(), since otherwise
- // getSystemService() will throw a ServiceNotFoundException.
- mWifiManager = mContext.getSystemService(WifiManager.class);
- }
-
// Only fetch WiFi power data if it is supported.
if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) {
SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi");
@@ -478,10 +495,6 @@
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
// We were asked to fetch Telephony data.
- if (mTelephony == null) {
- mTelephony = mContext.getSystemService(TelephonyManager.class);
- }
-
if (mTelephony != null) {
CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
mTelephony.requestModemActivityInfo(Runnable::run,
@@ -689,17 +702,91 @@
return delta;
}
- // TODO(b/172934873): Evaluate a safe way to query the HAL without holding mStats
+ /**
+ * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
+ * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s.
+ *
+ * @return array with true for index i if energy bucket i is supported.
+ */
+ private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
+ if (energyArray == null) {
+ return null;
+ }
+ final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
+ final int size = energyArray.size();
+ for (int energyIdx = 0; energyIdx < size; energyIdx++) {
+ switch (energyArray.getSubsystem(energyIdx)) {
+ case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
+ buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
+ buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
+ buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
+ break;
+ }
+ }
+ return buckets;
+ }
+
+ /**
+ * Get a {@link MeasuredEnergyArray} with the latest
+ * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot.
+ *
+ * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link
+ * EnergyConsumerResult}[]
+ */
+ private static @Nullable
+ MeasuredEnergyArray getEnergyConsumptionData(@NonNull PowerStatsInternal psi) {
+ final EnergyConsumerResult[] results;
+ try {
+ results = psi.getEnergyConsumedAsync(new int[0])
+ .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getEnergyConsumedAsync", e);
+ return null;
+ }
+ if (results == null) return null;
+ final int size = results.length;
+ final int[] subsystems = new int[size];
+ final long[] energyUJ = new long[size];
+
+ for (int i = 0; i < size; i++) {
+ final EnergyConsumerResult consumer = results[i];
+ final int subsystem;
+ switch (consumer.energyConsumerId) {
+ case EnergyConsumerId.DISPLAY:
+ subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
+ break;
+ default:
+ continue;
+ }
+ subsystems[i] = subsystem;
+ energyUJ[i] = consumer.energyUWs;
+ }
+ return new MeasuredEnergyArray() {
+ @Override
+ public int getSubsystem(int index) {
+ return subsystems[index];
+ }
+
+ @Override
+ public long getEnergy(int index) {
+ return energyUJ[index];
+ }
+
+ @Override
+ public int size() {
+ return size;
+ }
+ };
+ }
+
/** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */
@GuardedBy("mWorkerLock")
private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) {
- if (mMeasuredEnergySnapshot == null) return null;
+ if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
if (flags == UPDATE_ALL) {
// Gotta catch 'em all... including custom (non-specific) subsystems
- synchronized (mStats) {
- return mStats.getEnergyConsumptionDataLocked();
- }
+ return getEnergyConsumptionData(mPowerStatsInternal);
}
final List<Integer> energyConsumerIds = new ArrayList<>();
@@ -710,10 +797,8 @@
if (energyConsumerIds.isEmpty()) {
return null;
}
- synchronized (mStats) {
- // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
- return mStats.getEnergyConsumptionDataLocked();
- }
+ // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
+ return getEnergyConsumptionData(mPowerStatsInternal);
}
@GuardedBy("mWorkerLock")
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2f7c523..405ee72 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,14 +16,11 @@
package com.android.server.am;
-import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerId;
-import android.hardware.power.stats.EnergyConsumerResult;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
@@ -65,9 +62,6 @@
import com.android.internal.os.PowerProfile;
import com.android.internal.os.RailStats;
import com.android.internal.os.RpmStats;
-import com.android.internal.power.MeasuredEnergyArray;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
-import com.android.internal.power.MeasuredEnergyStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.ParseUtils;
@@ -75,7 +69,6 @@
import com.android.server.LocalServices;
import com.android.server.Watchdog;
import com.android.server.pm.UserManagerInternal;
-import com.android.server.powerstats.PowerStatsHALWrapper;
import java.io.File;
import java.io.FileDescriptor;
@@ -126,8 +119,6 @@
private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
- private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper;
-
private final HandlerThread mHandlerThread;
private final Handler mHandler;
private final Object mLock = new Object();
@@ -199,43 +190,6 @@
}
}
- @Override
- public @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
- final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
- if (results == null) return null;
- final int size = results.length;
- final int[] subsystems = new int[size];
- final long[] energyUJ = new long[size];
-
- for (int i = 0; i < size; i++) {
- final EnergyConsumerResult consumer = results[i];
- final int subsystem;
- switch (consumer.energyConsumerId) {
- case EnergyConsumerId.DISPLAY:
- subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
- break;
- default:
- continue;
- }
- subsystems[i] = subsystem;
- energyUJ[i] = consumer.energyUWs;
- }
- return new MeasuredEnergyArray() {
- @Override
- public int getSubsystem(int index) {
- return subsystems[index];
- }
- @Override
- public long getEnergy(int index) {
- return energyUJ[index];
- }
- @Override
- public int size() {
- return size;
- }
- };
- }
-
BatteryStatsService(Context context, File systemDir, Handler handler) {
// BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
mContext = context;
@@ -253,14 +207,9 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
- // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries
- mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
-
- final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData();
- final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies);
mStats = new BatteryStatsImpl(systemDir, handler, this,
- this, supportedBuckets, mUserManagerUserInfoProvider);
- mWorker = new BatteryExternalStatsWorker(context, mStats, initialEnergies);
+ this, mUserManagerUserInfoProvider);
+ mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
@@ -268,30 +217,6 @@
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
}
- /**
- * Map the {@link MeasuredEnergySubsystem}s in the given energyArray to their corresponding
- * {@link MeasuredEnergyStats.EnergyBucket}s.
- *
- * @return array with true for index i if energy bucket i is supported.
- */
- private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
- if (energyArray == null) {
- return null;
- }
- final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
- final int size = energyArray.size();
- for (int energyIdx = 0; energyIdx < size; energyIdx++) {
- switch (energyArray.getSubsystem(energyIdx)) {
- case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
- buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
- buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
- buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
- break;
- }
- }
- return buckets;
- }
-
public void publish() {
LocalServices.addService(BatteryStatsInternal.class, new LocalService());
ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
@@ -299,6 +224,7 @@
public void systemServicesReady() {
mStats.systemServicesReady(mContext);
+ mWorker.systemServicesReady();
Watchdog.getInstance().addMonitor(this);
}
@@ -2270,7 +2196,6 @@
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
- null /* energy buckets not currently in checkin anyway */,
mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
@@ -2311,7 +2236,6 @@
in.setDataPosition(0);
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
- null /* energy buckets not currently in checkin anyway */,
mUserManagerUserInfoProvider);
checkinStats.readSummaryFromParcel(in);
in.recycle();
diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
index 1f90393..3aca4cf 100644
--- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
@@ -28,7 +28,7 @@
final class FgsStartTempAllowList {
private static final int MAX_SIZE = 100;
/**
- * The key is the UID, the value is expiration elapse time in ms of this temp-allowed UID.
+ * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid.
*/
private final SparseLongArray mTempAllowListFgs = new SparseLongArray();
@@ -37,7 +37,8 @@
void add(int uid, long duration) {
if (duration <= 0) {
- Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid);
+ Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: "
+ + uid);
return;
}
// The temp allowlist should be a short list with only a few entries in it.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d69bf2d..de79315 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -92,7 +92,6 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LongSparseArray;
@@ -2731,11 +2730,11 @@
}
@GuardedBy("mService")
- final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
+ final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
boolean changed = false;
for (int i = mActiveUids.size() - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
- if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
+ if (uidRec.uid == uid && uidRec.curWhitelist != onWhitelist) {
uidRec.curWhitelist = onWhitelist;
changed = true;
}
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 676fcd0..17fd32c 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -1488,7 +1488,7 @@
private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
@NonNull TypedXmlSerializer serializer) throws IOException {
serializer.startTag(null, TAG_PACKAGE);
- serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
+ serializer.attributeInterned(null, ATTR_NAME, packageOps.getPackageName());
final int numAttributions = packageOps.getAttributedOpsCount();
for (int i = 0; i < numAttributions; i++) {
final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i);
diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
index 2a3cc90..5fa7998 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -22,6 +22,7 @@
import android.media.metrics.PlaybackErrorEvent;
import android.media.metrics.PlaybackMetrics;
import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
import android.os.Binder;
import android.util.Base64;
import android.util.StatsEvent;
@@ -120,5 +121,30 @@
.build();
StatsLog.write(statsEvent);
}
+
+ @Override
+ public void reportTrackChangeEvent(
+ String sessionId, TrackChangeEvent event, int userId) {
+ StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(321)
+ .writeString(sessionId)
+ .writeInt(event.getTrackState())
+ .writeInt(event.getTrackChangeReason())
+ .writeString(event.getContainerMimeType())
+ .writeString(event.getSampleMimeType())
+ .writeString(event.getCodecName())
+ .writeInt(event.getBitrate())
+ .writeLong(event.getTimeSincePlaybackCreatedMillis())
+ .writeInt(event.getTrackType())
+ .writeString(event.getLanguage())
+ .writeString(event.getLanguageRegion())
+ .writeInt(event.getChannelCount())
+ .writeInt(event.getSampleRate())
+ .writeInt(event.getWidth())
+ .writeInt(event.getHeight())
+ .usePooledBuffer()
+ .build();
+ StatsLog.write(statsEvent);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b65fc73..2b5c393 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -71,6 +71,7 @@
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
+import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFile;
import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
@@ -186,6 +187,7 @@
static final String TAG_CHILD_SESSION = "childSession";
static final String TAG_SESSION_FILE = "sessionFile";
static final String TAG_SESSION_CHECKSUM = "sessionChecksum";
+ static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature";
private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
"whitelisted-restricted-permission";
@@ -319,6 +321,8 @@
private float mProgress = 0;
@GuardedBy("mLock")
private float mReportedProgress = -1;
+ @GuardedBy("mLock")
+ private float mIncrementalProgress = 0;
/** State of the session. */
@GuardedBy("mLock")
@@ -397,8 +401,26 @@
@GuardedBy("mLock")
private ArraySet<FileEntry> mFiles = new ArraySet<>();
+ static class PerFileChecksum {
+ private final Checksum[] mChecksums;
+ private final byte[] mSignature;
+
+ PerFileChecksum(Checksum[] checksums, byte[] signature) {
+ mChecksums = checksums;
+ mSignature = signature;
+ }
+
+ Checksum[] getChecksums() {
+ return this.mChecksums;
+ }
+
+ byte[] getSignature() {
+ return this.mSignature;
+ }
+ }
+
@GuardedBy("mLock")
- private ArrayMap<String, Checksum[]> mChecksums = new ArrayMap<>();
+ private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
@Nullable
final StagedSession mStagedSession;
@@ -921,7 +943,7 @@
int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
SessionParams params, long createdMillis, long committedMillis,
File stageDir, String stageCid, InstallationFile[] files,
- ArrayMap<String, List<Checksum>> checksums,
+ ArrayMap<String, PerFileChecksum> checksums,
boolean prepared, boolean committed, boolean destroyed, boolean sealed,
@Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -967,11 +989,7 @@
}
if (checksums != null) {
- for (int i = 0, isize = checksums.size(); i < isize; ++i) {
- final String fileName = checksums.keyAt(i);
- final List<Checksum> fileChecksums = checksums.valueAt(i);
- mChecksums.put(fileName, fileChecksums.toArray(new Checksum[fileChecksums.size()]));
- }
+ mChecksums.putAll(checksums);
}
if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
@@ -1182,7 +1200,12 @@
@GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
- mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ // This method is triggered when the client progress is updated or the incremental progress
+ // is updated. For incremental installs, ignore the progress values reported from client.
+ // Instead, only use the progress reported by IncFs as the percentage of loading completion.
+ final float loadingProgress =
+ isIncrementalInstallation() ? mIncrementalProgress : mClientProgress;
+ mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f)
+ MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
// Only publish when meaningful change
@@ -1253,7 +1276,8 @@
}
@Override
- public void addChecksums(String name, @NonNull Checksum[] checksums) {
+ public void setChecksums(String name, @NonNull Checksum[] checksums,
+ @Nullable byte[] signature) {
if (checksums.length == 0) {
return;
}
@@ -1269,6 +1293,17 @@
throw new IllegalStateException("Can't obtain calling installer's package.");
}
+ if (signature != null && signature.length != 0) {
+ final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+ final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+ if (!standardMode || legacyMode) {
+ Slog.e(TAG,
+ "Can't enforce checksum's signature: Apk-Verity is disabled or in legacy "
+ + "mode.");
+ signature = null;
+ }
+ }
+
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
@@ -1277,7 +1312,7 @@
throw new IllegalStateException("Duplicate checksums.");
}
- mChecksums.put(name, checksums);
+ mChecksums.put(name, new PerFileChecksum(checksums, signature));
}
}
@@ -3032,15 +3067,25 @@
maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile);
}
+ private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
+ final byte[] bytes) throws IOException {
+ if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+ FileUtils.bytesToFile(absolutePath, bytes);
+ } else {
+ mIncrementalFileStorages.makeFile(localPath, bytes);
+ }
+ }
+
@GuardedBy("mLock")
private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName)
throws PackageManagerException {
- final Checksum[] checksums = mChecksums.get(origFile.getName());
- if (checksums == null) {
+ final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName());
+ if (perFileChecksum == null) {
return;
}
mChecksums.remove(origFile.getName());
+ final Checksum[] checksums = perFileChecksum.getChecksums();
if (checksums.length == 0) {
return;
}
@@ -3048,14 +3093,24 @@
final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
final File targetDigestsFile = new File(stageDir, targetDigestsPath);
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ // Storing and staging checksums.
ApkChecksums.writeChecksums(os, checksums);
- final byte[] checksumsBytes = os.toByteArray();
+ storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
+ os.toByteArray());
+ stageFileLocked(targetDigestsFile, targetDigestsFile);
- if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
- FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
- } else {
- mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
+ final byte[] signature = perFileChecksum.getSignature();
+ if (signature == null || signature.length == 0) {
+ return;
}
+
+ // Storing and staging signature.
+ final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath(
+ targetDigestsPath);
+ final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
+ storeBytesToInstallationFile(targetDigestsSignaturePath,
+ targetDigestsSignatureFile.getAbsolutePath(), signature);
+ stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
} catch (CertificateException e) {
throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
"Failed to encode certificate for " + mPackageName, e);
@@ -3063,8 +3118,6 @@
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to store digests for " + mPackageName, e);
}
-
- stageFileLocked(targetDigestsFile, targetDigestsFile);
}
@GuardedBy("mLock")
@@ -3704,7 +3757,16 @@
try {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
params, statusListener, healthCheckParams, healthListener, addedFiles,
- perUidReadTimeouts);
+ perUidReadTimeouts,
+ new IPackageLoadingProgressCallback.Stub() {
+ @Override
+ public void onPackageLoadingProgressChanged(float progress) {
+ synchronized (mLock) {
+ mIncrementalProgress = progress;
+ computeProgressLocked(true);
+ }
+ }
+ });
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
@@ -4277,7 +4339,8 @@
for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
final String fileName = mChecksums.keyAt(i);
- final Checksum[] checksums = mChecksums.valueAt(i);
+ final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
+ final Checksum[] checksums = perFileChecksum.getChecksums();
for (Checksum checksum : checksums) {
out.startTag(null, TAG_SESSION_CHECKSUM);
writeStringAttribute(out, ATTR_NAME, fileName);
@@ -4286,6 +4349,19 @@
out.endTag(null, TAG_SESSION_CHECKSUM);
}
}
+ for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
+ final String fileName = mChecksums.keyAt(i);
+ final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
+ final byte[] signature = perFileChecksum.getSignature();
+ if (signature == null || signature.length == 0) {
+ continue;
+ }
+ out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
+ writeStringAttribute(out, ATTR_NAME, fileName);
+ writeByteArrayAttribute(out, ATTR_SIGNATURE, signature);
+ out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
+ }
+
}
out.endTag(null, TAG_SESSION);
@@ -4401,6 +4477,7 @@
List<Integer> childSessionIds = new ArrayList<>();
List<InstallationFile> files = new ArrayList<>();
ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>();
+ ArrayMap<String, byte[]> signatures = new ArrayMap<>();
int outerDepth = in.getDepth();
int type;
while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -4443,6 +4520,11 @@
}
fileChecksums.add(checksum);
}
+ if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) {
+ final String fileName = readStringAttribute(in, ATTR_NAME);
+ final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE);
+ signatures.put(fileName, signature);
+ }
}
if (grantedRuntimePermissions.size() > 0) {
@@ -4471,13 +4553,25 @@
fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
}
+ ArrayMap<String, PerFileChecksum> checksumsMap = null;
+ if (!checksums.isEmpty()) {
+ checksumsMap = new ArrayMap<>(checksums.size());
+ for (int i = 0, isize = checksums.size(); i < isize; ++i) {
+ final String fileName = checksums.keyAt(i);
+ final List<Checksum> perFileChecksum = checksums.valueAt(i);
+ final byte[] perFileSignature = signatures.get(fileName);
+ checksumsMap.put(fileName, new PerFileChecksum(
+ perFileChecksum.toArray(new Checksum[perFileChecksum.size()]),
+ perFileSignature));
+ }
+ }
+
InstallSource installSource = InstallSource.create(installInitiatingPackageName,
installOriginatingPackageName, installerPackageName, installerAttributionTag);
- return new PackageInstallerSession(callback, context, pm, sessionProvider,
- installerThread, stagingManager, sessionId, userId, installerUid,
- installSource, params, createdMillis, committedMillis, stageDir, stageCid,
- fileArray, checksums, prepared, committed, destroyed, sealed, childSessionIdsArray,
- parentSessionId, isReady, isFailed, isApplied, stagedSessionErrorCode,
- stagedSessionErrorMessage);
+ return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread,
+ stagingManager, sessionId, userId, installerUid, installSource, params,
+ createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap,
+ prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
+ isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index de7338f..c93127d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -464,7 +464,7 @@
/**
* Keep track of all those APKs everywhere.
* <p>
- * Internally there are two important locks:
+ * Internally there are three important locks:
* <ul>
* <li>{@link #mLock} is used to guard all in-memory parsed package details
* and other related state. It is a fine-grained lock that should only be held
@@ -475,6 +475,10 @@
* this lock should never be acquired while already holding {@link #mLock}.
* Conversely, it's safe to acquire {@link #mLock} momentarily while already
* holding {@link #mInstallLock}.
+ * <li>{@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot
+ * itself and the snapshot invalidation flag. This lock should never be acquired while
+ * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock}
+ * momentarily while already holding {@link #mSnapshotLock}.
* </ul>
* Many internal methods rely on the caller to hold the appropriate locks, and
* this contract is expressed through method name suffixes:
@@ -485,6 +489,8 @@
* <li>fooLPr(): the caller must hold {@link #mLock} for reading
* <li>fooLPw(): the caller must hold {@link #mLock} for writing
* </ul>
+ * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}. It
+ * should not be taken anywhere else or used for any other purpose.
* <p>
* Because this class is very central to the platform's security; please run all
* CTS and unit tests whenever making modifications:
@@ -4727,11 +4733,19 @@
// If true, the cached computer object is invalid (the cache is stale).
// The attribute is static since it may be set from outside classes.
private static volatile boolean sSnapshotInvalid = true;
- // If true, the cache is corked. Do not create a new cache but continue to use the
+ // If true, the cache is corked. Do not create a new cache but continue to use the
// existing one. This throttles cache creation during periods of churn in Package
// Manager.
private static volatile boolean sSnapshotCorked = false;
+ /**
+ * This lock is used to make reads from {@link #sSnapshotInvalid} and
+ * {@link #mSnapshotComputer} atomic inside {@code snapshotComputer()}. This lock is
+ * not meant to be used outside that method. This lock must be taken before
+ * {@link #mLock} is taken.
+ */
+ private final Object mSnapshotLock = new Object();
+
// A counter of all queries that hit the cache.
private AtomicInteger mSnapshotHits = new AtomicInteger(0);
@@ -4759,35 +4773,42 @@
if (!SNAPSHOT_ENABLED) {
return mLiveComputer;
}
+ if (Thread.holdsLock(mLock)) {
+ // If the current thread holds mLock then it may have modified state but not
+ // yet invalidated the snapshot. Always give the thread the live computer.
+ return mLiveComputer;
+ }
int hits = 0;
if (TRACE_CACHES) {
hits = mSnapshotHits.incrementAndGet();
}
- Computer c = mSnapshotComputer;
- if (sSnapshotCorked && (c != null)) {
- // Snapshots are corked, which means new ones should not be built right now.
+ synchronized (mSnapshotLock) {
+ Computer c = mSnapshotComputer;
+ if (sSnapshotCorked && (c != null)) {
+ // Snapshots are corked, which means new ones should not be built right now.
+ return c;
+ }
+ if (sSnapshotInvalid || (c == null)) {
+ // The snapshot is invalid if it is marked as invalid or if it is null. If it
+ // is null, then it is currently being rebuilt by rebuildSnapshot().
+ synchronized (mLock) {
+ // Rebuild the snapshot if it is invalid. Note that the snapshot might be
+ // invalidated as it is rebuilt. However, the snapshot is still
+ // self-consistent (the lock is being held)and is current as of the time
+ // this function is entered.
+ if (sSnapshotInvalid) {
+ rebuildSnapshot(hits);
+ }
+
+ // Guaranteed to be non-null. mSnapshotComputer is only be set to null
+ // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since
+ // the mLock is held in this block and since rebuildSnapshot() is
+ // complete, the attribute can not now be null.
+ c = mSnapshotComputer;
+ }
+ }
return c;
}
- if (sSnapshotInvalid || (c == null)) {
- // The snapshot is invalid if it is marked as invalid or if it is null. If it
- // is null, then it is currently being rebuilt by rebuildSnapshot().
- synchronized (mLock) {
- // Rebuild the snapshot if it is invalid. Note that the snapshot might be
- // invalidated as it is rebuilt. However, the snapshot is still
- // self-consistent (the lock is being held)and is current as of the time
- // this function is entered.
- if (sSnapshotInvalid) {
- rebuildSnapshot(hits);
- }
-
- // Guaranteed to be non-null. mSnapshotComputer is only be set to null
- // temporarily in rebuildSnapshot(), which is guarded by mLock(). Since
- // the mLock is held in this block and since rebuildSnapshot() is
- // complete, the attribute can not now be null.
- c = mSnapshotComputer;
- }
- }
- return c;
}
/**
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 9f07695..89729b5 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1049,6 +1049,7 @@
}
}
+ // TODO: update resource strings in AppSearch
// If this shortcut is not from a manifest, then update all resource IDs
// from resource names. (We don't allow resource strings for
// non-manifest at the moment, but icons can still be resources.)
@@ -1340,6 +1341,7 @@
* For all the text fields, refresh the string values if they're from resources.
*/
public void resolveResourceStrings() {
+ // TODO: update resource strings in AppSearch
final ShortcutService s = mShortcutUser.mService;
List<ShortcutInfo> changedShortcuts = null;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 95ce140..3c4457d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1565,6 +1565,7 @@
* resource-based strings.
*/
void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
+ // TODO: update resource names in AppSearch
final Resources publisherRes = injectGetResourcesForApplicationAsUser(
si.getPackage(), si.getUserId());
if (publisherRes != null) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 7778572..64bddcd 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -19,14 +19,19 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.PowerEntityInfo;
import android.os.Binder;
import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.UserHandle;
+import android.power.PowerStatsInternal;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.SystemService;
import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
@@ -36,6 +41,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
/**
* This class provides a system service that estimates system power usage
@@ -55,7 +61,6 @@
private final Injector mInjector;
private Context mContext;
- private IPowerStatsHALWrapper mPowerStatsHALWrapper;
@Nullable
private PowerStatsLogger mPowerStatsLogger;
@Nullable
@@ -65,6 +70,8 @@
@VisibleForTesting
static class Injector {
+ private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+
File createDataStoragePath() {
return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
DATA_STORAGE_SUBDIR);
@@ -86,6 +93,13 @@
return PowerStatsHALWrapper.getPowerStatsHalImpl();
}
+ IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() {
+ if (mPowerStatsHALWrapper == null) {
+ mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
+ }
+ return mPowerStatsHALWrapper;
+ }
+
PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
String meterFilename, String modelFilename, String residencyFilename,
IPowerStatsHALWrapper powerStatsHALWrapper) {
@@ -120,15 +134,15 @@
}
} else if (args.length == 0) {
pw.println("PowerStatsService dumpsys: available PowerEntityInfos");
- PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo();
+ PowerEntityInfo[] powerEntityInfo = getPowerStatsHal().getPowerEntityInfo();
PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw);
pw.println("PowerStatsService dumpsys: available ChannelInfos");
- ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+ ChannelInfo[] channelInfo = getPowerStatsHal().getEnergyMeterInfo();
ChannelInfoUtils.dumpsys(channelInfo, pw);
pw.println("PowerStatsService dumpsys: available EnergyConsumerIds");
- int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+ int[] energyConsumerId = getPowerStatsHal().getEnergyConsumerInfo();
EnergyConsumerIdUtils.dumpsys(energyConsumerId, pw);
}
}
@@ -144,27 +158,33 @@
@Override
public void onStart() {
+ if (getPowerStatsHal().isInitialized()) {
+ // Only create internal service if PowerStatsHal is available.
+ publishLocalService(PowerStatsInternal.class, new LocalService());
+ }
publishBinderService(Context.POWER_STATS_SERVICE, new BinderService());
}
private void onSystemServiceReady() {
- mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
-
- if (mPowerStatsHALWrapper.isInitialized()) {
- if (DEBUG) Slog.d(TAG, "Starting PowerStatsService");
+ if (getPowerStatsHal().isInitialized()) {
+ if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
// Only start logger and triggers if initialization is successful.
mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
mInjector.createModelFilename(), mInjector.createResidencyFilename(),
- mPowerStatsHALWrapper);
+ getPowerStatsHal());
mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
} else {
- Slog.e(TAG, "Initialization of PowerStatsHAL wrapper failed");
+ Slog.e(TAG, "Failed to start PowerStatsService loggers");
}
}
+ private IPowerStatsHALWrapper getPowerStatsHal() {
+ return mInjector.getPowerStatsHALWrapperImpl();
+ }
+
public PowerStatsService(Context context) {
this(context, new Injector());
}
@@ -175,4 +195,29 @@
mContext = context;
mInjector = injector;
}
+
+ private final class LocalService extends PowerStatsInternal {
+ private final Handler mHandler;
+
+ LocalService() {
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+ }
+
+ @Override
+ public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+ int[] energyConsumerIds) {
+ final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture<>();
+ mHandler.sendMessage(
+ PooledLambda.obtainMessage(PowerStatsService.this::getEnergyConsumedAsync,
+ future, energyConsumerIds));
+ return future;
+ }
+ }
+
+ private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
+ int[] energyConsumerIds) {
+ future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
+ }
}
diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..a2943f3
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/OWNERS
@@ -0,0 +1,4 @@
+dplotnikov@google.com
+kwekua@google.com
+omakoto@google.com
+yamasani@google.com
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..2e9e625
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/utils/quota/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 5f65440..a5039fb 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -82,6 +82,7 @@
private PowerStatsLogger mPowerStatsLogger;
private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() {
+ private TestPowerStatsHALWrapper mTestPowerStatsHALWrapper = new TestPowerStatsHALWrapper();
@Override
File createDataStoragePath() {
mDataStorageDir = null;
@@ -111,8 +112,8 @@
}
@Override
- IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
- return new TestPowerStatsHALWrapper();
+ IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() {
+ return mTestPowerStatsHALWrapper;
}
@Override
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 04b365f..717040a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import android.Manifest;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -3357,6 +3358,24 @@
*/
public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
+ /**
+ * Indicates that call filtering in Telecom is complete
+ *
+ * This method is called for a connection created via
+ * {@link ConnectionService#onCreateIncomingConnection} when call filtering completes in
+ * Telecom, including checking the blocked number db, per-contact settings, and custom call
+ * filtering apps.
+ *
+ * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is
+ * {@code true}, {@link #onDisconnect()} will be called soon after
+ * this is called.
+ * @param isInContacts Indicates whether the caller is in the user's contacts list.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { }
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b1ccb53..f86f9d5 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -147,6 +147,7 @@
private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+ private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC";
private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
private static final String SESSION_START_RTT = "CS.+RTT";
@@ -200,6 +201,7 @@
private static final int MSG_ADD_PARTICIPANT = 39;
private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
+ private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
private static Connection sNullConnection;
@@ -722,6 +724,22 @@
}
@Override
+ public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = isBlocked;
+ args.arg3 = isInContacts;
+ args.arg4 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED);
try {
@@ -1354,6 +1372,21 @@
}
break;
}
+ case MSG_ON_CALL_FILTERING_COMPLETED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg4,
+ SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
+ String callId = (String) args.arg1;
+ boolean isBlocked = (boolean) args.arg2;
+ boolean isInContacts = (boolean) args.arg3;
+ onCallFilteringCompleted(callId, isBlocked, isInContacts);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_HANDOVER_COMPLETE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -2345,6 +2378,14 @@
}
}
+ private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
+ Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts);
+ Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
+ if (connection != null) {
+ connection.onCallFilteringCompleted(isBlocked, isInContacts);
+ }
+ }
+
/**
* Notifies a {@link Connection} that a handover has completed.
*
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 52210a5..feb2ca5 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -16,8 +16,10 @@
package android.telecom;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -1198,6 +1200,28 @@
}
/**
+ * Notifies this {@link RemoteConnection} that call filtering has completed, as well as
+ * the results of a contacts lookup for the remote party.
+ * @param isBlocked Whether call filtering indicates that the call should be blocked
+ * @param isInContacts Whether the remote party is in the user's contacts
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
+ Log.startSession("RC.oCFC", getActiveOwnerInfo());
+ try {
+ if (mConnected) {
+ mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
+ null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ /**
* Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
* upgrade request sent via {@link Connection#sendRemoteRttRequest}.
* Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index fb54179..92264be 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -118,6 +118,9 @@
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
+ void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+ in Session.Info sessionInfo);
+
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
void startRtt(String callId, in ParcelFileDescriptor fromInCall,
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 3c08d34..c04ddd7 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -16,6 +16,7 @@
"frameworks-base-testutils",
"framework-protos",
"mockito-target-minus-junit4",
+ "net-tests-utils",
"platform-test-annotations",
"services.core",
],
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
new file mode 100644
index 0000000..9c6b719
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.concurrent.Executor;
+
+public class VcnManagerTest {
+ private static final Executor INLINE_EXECUTOR = Runnable::run;
+
+ private IVcnManagementService mMockVcnManagementService;
+ private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
+
+ private Context mContext;
+ private VcnManager mVcnManager;
+
+ @Before
+ public void setUp() {
+ mMockVcnManagementService = mock(IVcnManagementService.class);
+ mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
+
+ mContext = getContext();
+ mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
+ }
+
+ @Test
+ public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+ ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor =
+ ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
+ verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
+
+ assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+
+ IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
+ listenerWrapper.onPolicyChanged();
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ verify(mMockVcnManagementService)
+ .addVcnUnderlyingNetworkPolicyListener(
+ any(IVcnUnderlyingNetworkPolicyListener.class));
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+ verify(mMockVcnManagementService, never())
+ .addVcnUnderlyingNetworkPolicyListener(
+ any(IVcnUnderlyingNetworkPolicyListener.class));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception {
+ mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() {
+ mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null);
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
new file mode 100644
index 0000000..3ba0a1f
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.NetworkCapabilities;
+
+import org.junit.Test;
+
+public class VcnUnderlyingNetworkPolicyTest {
+ private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY =
+ new VcnUnderlyingNetworkPolicy(
+ false /* isTearDownRequested */, new NetworkCapabilities());
+ private static final VcnUnderlyingNetworkPolicy SAMPLE_NETWORK_POLICY =
+ new VcnUnderlyingNetworkPolicy(
+ true /* isTearDownRequested */,
+ new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+ .build());
+
+ @Test
+ public void testEquals() {
+ assertEquals(DEFAULT_NETWORK_POLICY, DEFAULT_NETWORK_POLICY);
+ assertEquals(SAMPLE_NETWORK_POLICY, SAMPLE_NETWORK_POLICY);
+
+ assertNotEquals(DEFAULT_NETWORK_POLICY, SAMPLE_NETWORK_POLICY);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ assertParcelSane(SAMPLE_NETWORK_POLICY, 2);
+ }
+}