Merge "Delete deprecated methods after dogfood transition." into sc-dev
diff --git a/Android.bp b/Android.bp
index d75239a..7c9cdcf 100644
--- a/Android.bp
+++ b/Android.bp
@@ -483,6 +483,19 @@
"--api-lint-ignore-prefix junit. " +
"--api-lint-ignore-prefix org. "
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
filegroup {
name: "android-non-updatable-stub-sources",
srcs: [
@@ -535,7 +548,7 @@
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.vibrator-V1.3-java",
"framework-protos",
- "stable.core.platform.api.stubs",
+ "art.module.public.api",
// There are a few classes from modules used by the core that
// need to be resolved by metalava. We use a prebuilt stub of the
// full sdk to ensure we can resolve them. If a new class gets added,
@@ -544,6 +557,7 @@
// NOTE: The below can be removed once the prebuilt stub contains IKE.
"sdk_system_current_android.net.ipsec.ike",
],
+ filter_packages: packages_to_document,
high_mem: true, // Lots of sources => high memory use, see b/170701554
installable: false,
annotations_enabled: true,
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 0f218b5..0aed5d9 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -93,13 +93,14 @@
":updatable-media-srcs",
// No longer part of the stubs, but are included in the docs.
- "test-base/src/**/*.java",
- "test-mock/src/**/*.java",
- "test-runner/src/**/*.java",
+ ":android-test-base-sources",
+ ":android-test-mock-sources",
+ ":android-test-runner-sources",
],
libs: framework_docs_only_libs,
create_doc_stubs: true,
annotations_enabled: true,
+ filter_packages: packages_to_document,
api_levels_annotations_enabled: true,
api_levels_annotations_dirs: [
"sdk-dir",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 7b8a687..ed24d43 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -27,24 +27,10 @@
// Common metalava configs
/////////////////////////////////////////////////////////////////////
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
stubs_defaults {
name: "metalava-non-updatable-api-stubs-default",
defaults: ["android-non-updatable-stubs-defaults"],
api_levels_annotations_enabled: false,
- filter_packages: packages_to_document,
defaults_visibility: ["//visibility:private"],
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
deleted file mode 100644
index 4686de8..0000000
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright 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.app.appsearch;
-
-/** {@hide} */
-parcelable AppSearchBatchResult;
\ No newline at end of file
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index c4dd1a0..bc3c79f 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
@@ -18,6 +18,7 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SystemService;
+import android.app.appsearch.aidl.IAppSearchManager;
import android.content.Context;
import com.android.internal.util.Preconditions;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
index ec0533e..7dc527a 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java
@@ -17,6 +17,7 @@
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
+import android.app.appsearch.aidl.IAppSearchManager;
import android.content.Context;
/**
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
index 0089c6d..353051c 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java
@@ -23,6 +23,9 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.appsearch.exceptions.AppSearchException;
import android.os.Bundle;
import android.os.Parcel;
@@ -105,8 +108,8 @@
mUserId,
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
}
});
AppSearchResult<Void> result = future.get();
@@ -145,8 +148,8 @@
mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserId,
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ future.complete(resultParcel.getResult());
}
});
AppSearchResult<List<Bundle>> result = future.get();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index c112d0e..5910130 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -19,6 +19,11 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.appsearch.exceptions.AppSearchException;
import android.app.appsearch.util.SchemaMigrationUtil;
import android.compat.annotation.UnsupportedAppUsage;
@@ -86,13 +91,15 @@
@NonNull Consumer<AppSearchResult<AppSearchSession>> callback) {
try {
mService.initialize(mUserId, new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Void> result = resultParcel.getResult();
if (result.isSuccess()) {
callback.accept(
AppSearchResult.newSuccessfulResult(AppSearchSession.this));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -191,15 +198,16 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
- Bundle responseBundle = (Bundle) result.getResultValue();
GetSchemaResponse response =
- new GetSchemaResponse(responseBundle);
+ new GetSchemaResponse(result.getResultValue());
callback.accept(AppSearchResult.newSuccessfulResult(response));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -227,15 +235,17 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<List<String>> result = resultParcel.getResult();
if (result.isSuccess()) {
Set<String> namespaces =
- new ArraySet<>((List<String>) result.getResultValue());
+ new ArraySet<>(result.getResultValue());
callback.accept(
AppSearchResult.newSuccessfulResult(namespaces));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -280,13 +290,14 @@
/*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
- executor.execute(() -> callback.onResult(result));
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
+ executor.execute(() -> callback.onResult(resultParcel.getResult()));
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> sendSystemErrorToCallback(
+ resultParcel.getResult(), callback));
}
});
mIsMutated = true;
@@ -341,15 +352,17 @@
mUserId,
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchBatchResult<String, Bundle> result =
+ resultParcel.getResult();
AppSearchBatchResult.Builder<String, GenericDocument>
documentResultBuilder =
new AppSearchBatchResult.Builder<>();
// Translate successful results
for (Map.Entry<String, Bundle> bundleEntry :
- ((Map<String, Bundle>) result.getSuccesses()).entrySet()) {
+ result.getSuccesses().entrySet()) {
GenericDocument document;
try {
document = new GenericDocument(bundleEntry.getValue());
@@ -380,8 +393,9 @@
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel result) {
+ executor.execute(
+ () -> sendSystemErrorToCallback(result.getResult(), callback));
}
});
} catch (RemoteException e) {
@@ -492,8 +506,9 @@
/*systemUsage=*/ false,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
@@ -550,13 +565,14 @@
new ArrayList<>(request.getIds()), mUserId,
new IAppSearchBatchResultCallback.Stub() {
@Override
- public void onResult(AppSearchBatchResult result) {
- executor.execute(() -> callback.onResult(result));
+ public void onResult(AppSearchBatchResultParcel resultParcel) {
+ executor.execute(() -> callback.onResult(resultParcel.getResult()));
}
@Override
- public void onSystemError(AppSearchResult result) {
- executor.execute(() -> sendSystemErrorToCallback(result, callback));
+ public void onSystemError(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> sendSystemErrorToCallback(
+ resultParcel.getResult(), callback));
}
});
mIsMutated = true;
@@ -598,8 +614,9 @@
mService.removeByQuery(mPackageName, mDatabaseName, queryExpression,
searchSpec.getBundle(), mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
@@ -629,15 +646,15 @@
mDatabaseName,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
- Bundle responseBundle = (Bundle) result.getResultValue();
- StorageInfo response =
- new StorageInfo(responseBundle);
+ StorageInfo response = new StorageInfo(result.getResultValue());
callback.accept(AppSearchResult.newSuccessfulResult(response));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -686,13 +703,14 @@
request.getVersion(),
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Bundle> result = resultParcel.getResult();
if (result.isSuccess()) {
try {
SetSchemaResponse setSchemaResponse =
- new SetSchemaResponse(
- (Bundle) result.getResultValue());
+ new SetSchemaResponse(result.getResultValue());
if (!request.isForceOverride()) {
// Throw exception if there is any deleted types or
// incompatible types. That's the only case we swallowed
@@ -707,7 +725,7 @@
callback.accept(AppSearchResult.throwableToFailedResult(t));
}
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -772,8 +790,9 @@
request.getVersion(),
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- setSchemaFuture.complete(result);
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ setSchemaFuture.complete(resultParcel.getResult());
}
});
AppSearchResult<Bundle> setSchemaResult = setSchemaFuture.get();
@@ -823,8 +842,8 @@
mUserId,
new IAppSearchResultCallback.Stub() {
@Override
- public void onResult(AppSearchResult result) {
- setSchema2Future.complete(result);
+ public void onResult(AppSearchResultParcel resultParcel) {
+ setSchema2Future.complete(resultParcel.getResult());
}
});
AppSearchResult<Bundle> setSchema2Result = setSchema2Future.get();
diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
index d49f472..7d246c2 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java
@@ -16,10 +16,12 @@
package android.app.appsearch;
-
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.RemoteException;
import android.util.Log;
@@ -71,13 +73,15 @@
@NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
try {
mService.initialize(mUserId, new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
executor.execute(() -> {
+ AppSearchResult<Void> result = resultParcel.getResult();
if (result.isSuccess()) {
callback.accept(
AppSearchResult.newSuccessfulResult(GlobalSearchSession.this));
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(result));
}
});
}
@@ -159,8 +163,9 @@
/*systemUsage=*/ true,
mUserId,
new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> callback.accept(result));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> callback.accept(resultParcel.getResult()));
}
});
mIsMutated = true;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 531c984..4368672 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -20,6 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;
@@ -139,18 +142,20 @@
@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
return new IAppSearchResultCallback.Stub() {
- public void onResult(AppSearchResult result) {
- executor.execute(() -> invokeCallback(result, callback));
+ @Override
+ public void onResult(AppSearchResultParcel resultParcel) {
+ executor.execute(() -> invokeCallback(resultParcel.getResult(), callback));
}
};
}
- private void invokeCallback(AppSearchResult result,
+ private void invokeCallback(
+ @NonNull AppSearchResult<Bundle> searchResultPageResult,
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
- if (result.isSuccess()) {
+ if (searchResultPageResult.isSuccess()) {
try {
SearchResultPage searchResultPage =
- new SearchResultPage((Bundle) result.getResultValue());
+ new SearchResultPage(searchResultPageResult.getResultValue());
mNextPageToken = searchResultPage.getNextPageToken();
callback.accept(AppSearchResult.newSuccessfulResult(
searchResultPage.getResults()));
@@ -158,7 +163,7 @@
callback.accept(AppSearchResult.throwableToFailedResult(t));
}
} else {
- callback.accept(result);
+ callback.accept(AppSearchResult.newFailedResult(searchResultPageResult));
}
}
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
similarity index 80%
copy from apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
copy to apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
index 37ce990..89908cd 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
/** {@hide} */
-parcelable AppSearchResult<ValueType>;
\ No newline at end of file
+parcelable AppSearchBatchResultParcel<ValueType>;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java
new file mode 100644
index 0000000..b0cc10c
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java
@@ -0,0 +1,97 @@
+/*
+ * 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.app.appsearch.aidl;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
+import android.app.appsearch.AppSearchResult;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Parcelable wrapper around {@link AppSearchBatchResult}.
+ *
+ * <p>{@link AppSearchBatchResult} can contain any type of key and value, including non-parcelable
+ * values. For the specific case of sending {@link AppSearchBatchResult} across Binder, this class
+ * wraps an {@link AppSearchBatchResult} that has String keys and Parcelable values. It provides
+ * parcelability of the whole structure.
+ *
+ * @param <ValueType> The type of result object for successful calls. Must be a parcelable type.
+ * @hide
+ */
+public final class AppSearchBatchResultParcel<ValueType> implements Parcelable {
+ private final AppSearchBatchResult<String, ValueType> mResult;
+
+ /** Creates a new {@link AppSearchBatchResultParcel} from the given result. */
+ public AppSearchBatchResultParcel(@NonNull AppSearchBatchResult<String, ValueType> result) {
+ mResult = Objects.requireNonNull(result);
+ }
+
+ private AppSearchBatchResultParcel(@NonNull Parcel in) {
+ Bundle bundle = in.readBundle();
+ AppSearchBatchResult.Builder<String, ValueType> builder =
+ new AppSearchBatchResult.Builder<>();
+ for (String key : bundle.keySet()) {
+ AppSearchResultParcel<ValueType> resultParcel = bundle.getParcelable(key);
+ builder.setResult(key, resultParcel.getResult());
+ }
+ mResult = builder.build();
+ }
+
+ @NonNull
+ public AppSearchBatchResult<String, ValueType> getResult() {
+ return mResult;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ Bundle bundle = new Bundle();
+ for (Map.Entry<String, AppSearchResult<ValueType>> entry
+ : mResult.getAll().entrySet()) {
+ bundle.putParcelable(entry.getKey(), new AppSearchResultParcel<>(entry.getValue()));
+ }
+ dest.writeBundle(bundle);
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @NonNull
+ public static final Creator<AppSearchBatchResultParcel<?>> CREATOR =
+ new Creator<AppSearchBatchResultParcel<?>>() {
+ @NonNull
+ @Override
+ public AppSearchBatchResultParcel<?> createFromParcel(@NonNull Parcel in) {
+ return new AppSearchBatchResultParcel<>(in);
+ }
+
+ @NonNull
+ @Override
+ public AppSearchBatchResultParcel<?>[] newArray(int size) {
+ return new AppSearchBatchResultParcel<?>[size];
+ }
+ };
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
similarity index 81%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
index 37ce990..4e35bd5 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2020, The Android Open Source Project
+ * Copyright 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
/** {@hide} */
-parcelable AppSearchResult<ValueType>;
\ No newline at end of file
+parcelable AppSearchResultParcel<ValueType>;
diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java
new file mode 100644
index 0000000..8b137d3
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java
@@ -0,0 +1,95 @@
+/*
+ * 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.app.appsearch.aidl;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Parcelable wrapper around {@link AppSearchResult}.
+ *
+ * <p>{@link AppSearchResult} can contain any value, including non-parcelable values. For the
+ * specific case of sending {@link AppSearchResult} across Binder, this class wraps an
+ * {@link AppSearchResult} that contains a parcelable type and provides parcelability of the whole
+ * structure.
+ *
+ * @param <ValueType> The type of result object for successful calls. Must be a parcelable type.
+ * @hide
+ */
+public final class AppSearchResultParcel<ValueType> implements Parcelable {
+ private final AppSearchResult<ValueType> mResult;
+
+ /** Creates a new {@link AppSearchResultParcel} from the given result. */
+ public AppSearchResultParcel(@NonNull AppSearchResult<ValueType> result) {
+ mResult = Objects.requireNonNull(result);
+ }
+
+ private AppSearchResultParcel(@NonNull Parcel in) {
+ int resultCode = in.readInt();
+ ValueType resultValue = (ValueType) in.readValue(/*loader=*/ null);
+ String errorMessage = in.readString();
+ if (resultCode == AppSearchResult.RESULT_OK) {
+ mResult = AppSearchResult.newSuccessfulResult(resultValue);
+ } else {
+ mResult = AppSearchResult.newFailedResult(resultCode, errorMessage);
+ }
+ }
+
+ @NonNull
+ public AppSearchResult<ValueType> getResult() {
+ return mResult;
+ }
+
+ /** @hide */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResult.getResultCode());
+ if (mResult.isSuccess()) {
+ dest.writeValue(mResult.getResultValue());
+ } else {
+ dest.writeValue(null);
+ }
+ dest.writeString(mResult.getErrorMessage());
+ }
+
+ /** @hide */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @NonNull
+ public static final Creator<AppSearchResultParcel<?>> CREATOR =
+ new Creator<AppSearchResultParcel<?>>() {
+ @NonNull
+ @Override
+ public AppSearchResultParcel<?> createFromParcel(@NonNull Parcel in) {
+ return new AppSearchResultParcel<>(in);
+ }
+
+ @NonNull
+ @Override
+ public AppSearchResultParcel<?>[] newArray(int size) {
+ return new AppSearchResultParcel<?>[size];
+ }
+ };
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
similarity index 70%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
index 64b331e..1fe19cc 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchBatchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl
@@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
/** {@hide} */
oneway interface IAppSearchBatchResultCallback {
- void onResult(in AppSearchBatchResult result);
- void onSystemError(in AppSearchResult result);
+ void onResult(in AppSearchBatchResultParcel resultParcel);
+ void onSystemError(in AppSearchResultParcel resultParcel);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
similarity index 98%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
index 507bd68..6f7e82e 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
import android.os.Bundle;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchResultCallback;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.os.ParcelFileDescriptor;
/** {@hide} */
@@ -200,7 +200,7 @@
* @param searchSpecBundle SearchSpec bundle.
* @param userId Id of the calling user.
* @param callback {@link IAppSearchResultCallback#onResult} will be called with an
- * {@link AppSearchResult}<{@code null}>.
+ * {@link AppSearchResult}<{@code Void}>.
*/
void writeQueryResultsToFile(
in String packageName,
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
similarity index 81%
rename from apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
rename to apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
index 299c9957..097f0d1 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchResultCallback.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.app.appsearch;
+package android.app.appsearch.aidl;
-import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.aidl.AppSearchResultParcel;
/** {@hide} */
oneway interface IAppSearchResultCallback {
- void onResult(in AppSearchResult result);
+ void onResult(in AppSearchResultParcel resultParcel);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
similarity index 75%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
rename to apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
index 9ae0d62..aca0fc0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
@@ -13,37 +13,35 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.app.appsearch;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.ArrayMap;
import com.android.internal.util.Preconditions;
-import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Provides results for AppSearch batch operations which encompass multiple documents.
*
- * <p>Individual results of a batch operation are separated into two maps: one for successes and
- * one for failures. For successes, {@link #getSuccesses()} will return a map of keys to
- * instances of the value type. For failures, {@link #getFailures()} will return a map of keys to
- * {@link AppSearchResult} objects.
+ * <p>Individual results of a batch operation are separated into two maps: one for successes and one
+ * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of
+ * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link
+ * AppSearchResult} objects.
*
* <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for
* both successes and failures.
*
+ * @param <KeyType> The type of the keys for which the results will be reported.
+ * @param <ValueType> The type of the result objects for successful results.
* @see AppSearchSession#put
* @see AppSearchSession#getByDocumentId
* @see AppSearchSession#remove
*/
-public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable {
+public final class AppSearchBatchResult<KeyType, ValueType> {
@NonNull private final Map<KeyType, ValueType> mSuccesses;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
@NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
@@ -57,27 +55,6 @@
mAll = all;
}
- private AppSearchBatchResult(@NonNull Parcel in) {
- mAll = Collections.unmodifiableMap(in.readHashMap(/*loader=*/ null));
- Map<KeyType, ValueType> successes = new ArrayMap<>();
- Map<KeyType, AppSearchResult<ValueType>> failures = new ArrayMap<>();
- for (Map.Entry<KeyType, AppSearchResult<ValueType>> entry : mAll.entrySet()) {
- if (entry.getValue().isSuccess()) {
- successes.put(entry.getKey(), entry.getValue().getResultValue());
- } else {
- failures.put(entry.getKey(), entry.getValue());
- }
- }
- mSuccesses = Collections.unmodifiableMap(successes);
- mFailures = Collections.unmodifiableMap(failures);
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeMap(mAll);
- }
-
/** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
public boolean isSuccess() {
return mFailures.isEmpty();
@@ -99,8 +76,8 @@
}
/**
- * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all
- * failed individual results.
+ * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed
+ * individual results.
*
* <p>The values of the {@link Map} will not be {@code null}.
*/
@@ -122,6 +99,7 @@
/**
* Asserts that this {@link AppSearchBatchResult} has no failures.
+ *
* @hide
*/
public void checkSuccess() {
@@ -136,33 +114,13 @@
return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}";
}
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @NonNull
- public static final Creator<AppSearchBatchResult> CREATOR =
- new Creator<AppSearchBatchResult>() {
- @NonNull
- @Override
- public AppSearchBatchResult createFromParcel(@NonNull Parcel in) {
- return new AppSearchBatchResult(in);
- }
-
- @NonNull
- @Override
- public AppSearchBatchResult[] newArray(int size) {
- return new AppSearchBatchResult[size];
- }
- };
-
/**
* Builder for {@link AppSearchBatchResult} objects.
*
* <p>Once {@link #build} is called, the instance can no longer be used.
+ *
+ * @param <KeyType> The type of the keys for which the results will be reported.
+ * @param <ValueType> The type of the result objects for successful results.
*/
public static final class Builder<KeyType, ValueType> {
private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>();
@@ -177,7 +135,7 @@
*
* @throws IllegalStateException if the builder has already been used.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
@NonNull
public Builder<KeyType, ValueType> setSuccess(
@NonNull KeyType key, @Nullable ValueType result) {
@@ -193,7 +151,7 @@
*
* @throws IllegalStateException if the builder has already been used.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
@NonNull
public Builder<KeyType, ValueType> setFailure(
@NonNull KeyType key,
@@ -211,7 +169,7 @@
*
* @throws IllegalStateException if the builder has already been used.
*/
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
+ @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
@NonNull
public Builder<KeyType, ValueType> setResult(
@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) {
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
similarity index 82%
rename from apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
rename to apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
index b06e215..30c98b0 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java
@@ -13,15 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.app.appsearch;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.appsearch.exceptions.AppSearchException;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -36,24 +33,26 @@
*
* @param <ValueType> The type of result object for successful calls.
*/
-public final class AppSearchResult<ValueType> implements Parcelable {
+public final class AppSearchResult<ValueType> {
private static final String TAG = "AppSearchResult";
/**
* Result codes from {@link AppSearchSession} methods.
+ *
* @hide
*/
- @IntDef(value = {
- RESULT_OK,
- RESULT_UNKNOWN_ERROR,
- RESULT_INTERNAL_ERROR,
- RESULT_INVALID_ARGUMENT,
- RESULT_IO_ERROR,
- RESULT_OUT_OF_SPACE,
- RESULT_NOT_FOUND,
- RESULT_INVALID_SCHEMA,
- RESULT_SECURITY_ERROR,
- })
+ @IntDef(
+ value = {
+ RESULT_OK,
+ RESULT_UNKNOWN_ERROR,
+ RESULT_INTERNAL_ERROR,
+ RESULT_INVALID_ARGUMENT,
+ RESULT_IO_ERROR,
+ RESULT_OUT_OF_SPACE,
+ RESULT_NOT_FOUND,
+ RESULT_INVALID_SCHEMA,
+ RESULT_SECURITY_ERROR,
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface ResultCode {}
@@ -109,20 +108,6 @@
mErrorMessage = errorMessage;
}
- private AppSearchResult(@NonNull Parcel in) {
- mResultCode = in.readInt();
- mResultValue = (ValueType) in.readValue(/*loader=*/ null);
- mErrorMessage = in.readString();
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mResultCode);
- dest.writeValue(mResultValue);
- dest.writeString(mErrorMessage);
- }
-
/** Returns {@code true} if {@link #getResultCode} equals {@link AppSearchResult#RESULT_OK}. */
public boolean isSuccess() {
return getResultCode() == RESULT_OK;
@@ -154,8 +139,8 @@
*
* <p>If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error
* message may be {@code null} even if {@link #isSuccess} is {@code false}. See the
- * documentation of the particular {@link AppSearchSession} call producing this
- * {@link AppSearchResult} for what is returned by {@link #getErrorMessage}.
+ * documentation of the particular {@link AppSearchSession} call producing this {@link
+ * AppSearchResult} for what is returned by {@link #getErrorMessage}.
*/
@Nullable
public String getErrorMessage() {
@@ -190,40 +175,14 @@
return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
}
- /** @hide */
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @NonNull
- public static final Creator<AppSearchResult> CREATOR = new Creator<AppSearchResult>() {
- @NonNull
- @Override
- public AppSearchResult createFromParcel(@NonNull Parcel in) {
- return new AppSearchResult(in);
- }
-
- @NonNull
- @Override
- public AppSearchResult[] newArray(int size) {
- return new AppSearchResult[size];
- }
- };
-
- /**
- * Creates a new successful {@link AppSearchResult}.
- */
+ /** Creates a new successful {@link AppSearchResult}. */
@NonNull
public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
@Nullable ValueType value) {
return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null);
}
- /**
- * Creates a new failed {@link AppSearchResult}.
- */
+ /** Creates a new failed {@link AppSearchResult}. */
@NonNull
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
@ResultCode int resultCode, @Nullable String errorMessage) {
@@ -238,7 +197,8 @@
@NonNull
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
@NonNull AppSearchResult<?> otherFailedResult) {
- Preconditions.checkState(!otherFailedResult.isSuccess(),
+ Preconditions.checkState(
+ !otherFailedResult.isSuccess(),
"Cannot convert a success result to a failed result");
return AppSearchResult.newFailedResult(
otherFailedResult.getResultCode(), otherFailedResult.getErrorMessage());
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index a4188a2..0f42d85 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -29,14 +29,16 @@
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetSchemaResponse;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchManager;
-import android.app.appsearch.IAppSearchResultCallback;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaResponse;
import android.app.appsearch.StorageInfo;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -869,7 +871,7 @@
private void invokeCallbackOnResult(
IAppSearchResultCallback callback, AppSearchResult<?> result) {
try {
- callback.onResult(result);
+ callback.onResult(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -877,9 +879,9 @@
/** Invokes the {@link IAppSearchBatchResultCallback} with the result. */
private void invokeCallbackOnResult(
- IAppSearchBatchResultCallback callback, AppSearchBatchResult<?, ?> result) {
+ IAppSearchBatchResultCallback callback, AppSearchBatchResult<String, ?> result) {
try {
- callback.onResult(result);
+ callback.onResult(new AppSearchBatchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -891,8 +893,9 @@
* <p>The throwable is convert to a {@link AppSearchResult};
*/
private void invokeCallbackOnError(IAppSearchResultCallback callback, Throwable throwable) {
+ AppSearchResult<?> result = throwableToFailedResult(throwable);
try {
- callback.onResult(throwableToFailedResult(throwable));
+ callback.onResult(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send result to the callback", e);
}
@@ -905,8 +908,9 @@
*/
private void invokeCallbackOnError(
@NonNull IAppSearchBatchResultCallback callback, @NonNull Throwable throwable) {
+ AppSearchResult<?> result = throwableToFailedResult(throwable);
try {
- callback.onSystemError(throwableToFailedResult(throwable));
+ callback.onSystemError(new AppSearchResultParcel<>(result));
} catch (RemoteException e) {
Log.e(TAG, "Unable to send error to the callback", e);
}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 9dd1296..42e953b 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -344,6 +344,9 @@
*/
public static final int REASON_MEDIA_SESSION_CALLBACK = 317;
+ /** @hide The app requests out-out. */
+ public static final int REASON_OPT_OUT_REQUESTED = 1000;
+
/**
* The list of BG-FGS-Launch and temp-allow-list reason code.
* @hide
@@ -411,6 +414,7 @@
REASON_EVENT_MMS,
REASON_SHELL,
REASON_MEDIA_SESSION_CALLBACK,
+ REASON_OPT_OUT_REQUESTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode {}
@@ -709,6 +713,8 @@
return "SHELL";
case REASON_MEDIA_SESSION_CALLBACK:
return "MEDIA_SESSION_CALLBACK";
+ case REASON_OPT_OUT_REQUESTED:
+ return "REASON_OPT_OUT_REQUESTED";
default:
return "(unknown:" + reasonCode + ")";
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index ebf4ed0..4b081d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -180,7 +180,7 @@
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
- COMPRESS_TIME ? 32 * ONE_MINUTE : 30 * ONE_DAY
+ COMPRESS_TIME ? 32 * ONE_MINUTE : 45 * ONE_DAY
};
/** The minimum allowed values for each index in {@link #DEFAULT_ELAPSED_TIME_THRESHOLDS}. */
diff --git a/build/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
similarity index 100%
rename from build/boot/boot-image-profile.txt
rename to boot/boot-image-profile.txt
diff --git a/build/boot/preloaded-classes b/boot/preloaded-classes
similarity index 100%
rename from build/boot/preloaded-classes
rename to boot/preloaded-classes
diff --git a/core/api/current.txt b/core/api/current.txt
index 9131816..324ad33 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52988,7 +52988,7 @@
method @NonNull public android.view.translation.TranslationRequestValue getValue(@NonNull String);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.ViewTranslationRequest> CREATOR;
- field public static final String ID_TEXT = "text";
+ field public static final String ID_TEXT = "android:text";
}
public static final class ViewTranslationRequest.Builder {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d366e35d..f458107 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -50,6 +50,10 @@
field public static final String UNDEFINED = "android.permission-group.UNDEFINED";
}
+ public static final class R.attr {
+ field public static final int requestForegroundServiceExemption;
+ }
+
public static final class R.bool {
field public static final int config_assistantOnTopOfDream = 17891333; // 0x1110005
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
@@ -452,7 +456,7 @@
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
- method public void forceUpdateUserSetupComplete();
+ method public void forceUpdateUserSetupComplete(int);
method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
method public long getLastBugReportRequestTime();
@@ -806,6 +810,7 @@
}
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method public boolean hasRequestForegroundServiceExemption();
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 02e64b8..38b19ae 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11974,15 +11974,14 @@
/**
* @hide
- * Force update user setup completed status.
+ * Force update user setup completed status for the given {@code userId}.
* @throws {@link SecurityException} if the caller has no
- * {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS} or the caller is
- * not {@link UserHandle#SYSTEM_USER}
+ * {@code android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}.
*/
@TestApi
- public void forceUpdateUserSetupComplete() {
+ public void forceUpdateUserSetupComplete(@UserIdInt int userId) {
try {
- mService.forceUpdateUserSetupComplete();
+ mService.forceUpdateUserSetupComplete(userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9f76bd1..db2fc0d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -406,7 +406,7 @@
boolean isDeviceProvisioningConfigApplied();
void setDeviceProvisioningConfigApplied();
- void forceUpdateUserSetupComplete();
+ void forceUpdateUserSetupComplete(int userId);
void setBackupServiceEnabled(in ComponentName admin, boolean enabled);
boolean isBackupServiceEnabled(in ComponentName admin);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 1cf4567..8c59982 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -189,6 +189,7 @@
* detected.
* <li> {@code eio} indicates that an I/O error will be returned for an attempt to read
* corrupted data blocks.
+ * <li> {@code disabled} indicates that integrity check is disabled.
* For details see Verified Boot documentation.
*/
public static final int TAG_OS_STARTUP = SecurityLogTags.SECURITY_OS_STARTUP;
@@ -344,7 +345,7 @@
public static final int TAG_WIPE_FAILURE = SecurityLogTags.SECURITY_WIPE_FAILED;
/**
- * Indicates that an authentication key was generated. The log entry contains the following
+ * Indicates that a cryptographic key was generated. The log entry contains the following
* information about the event, encapsulated in an {@link Object} array and accessible via
* {@link SecurityEvent#getData()}:
* <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index a3e0473..8b0e992 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -778,9 +778,18 @@
*/
public static final int PRIVATE_FLAG_EXT_PROFILEABLE = 1 << 0;
+ /**
+ * Value for {@link #privateFlagsExt}: whether this application has requested
+ * exemption from the foreground service restriction introduced in S
+ * (https://developer.android.com/about/versions/12/foreground-services).
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1 << 1;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
PRIVATE_FLAG_EXT_PROFILEABLE,
+ PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlagsExt {}
@@ -2445,6 +2454,17 @@
}
/**
+ * @return whether the app has requested exemption from the foreground service restrictions.
+ * This does not take any affect for now.
+ * @hide
+ */
+ @TestApi
+ public boolean hasRequestForegroundServiceExemption() {
+ return (privateFlagsExt
+ & ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION) != 0;
+ }
+
+ /**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index 980f10d..75fa1c1 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -806,7 +806,9 @@
public static int appInfoPrivateFlagsExt(ParsingPackageRead pkg) {
// @formatter:off
int privateFlagsExt =
- flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE);
+ flag(pkg.isProfileable(), ApplicationInfo.PRIVATE_FLAG_EXT_PROFILEABLE)
+ | flag(pkg.hasRequestForegroundServiceExemption(),
+ ApplicationInfo.PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION);
// @formatter:on
return privateFlagsExt;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index cea50cb..2413e6d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -338,6 +338,8 @@
ParsingPackage setTheme(int theme);
+ ParsingPackage setRequestForegroundServiceExemption(boolean requestForegroundServiceExemption);
+
ParsingPackage setUpgradeKeySets(@NonNull Set<String> upgradeKeySets);
ParsingPackage setUse32BitAbi(boolean use32BitAbi);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 7114886..b0342aa 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -464,6 +464,7 @@
CROSS_PROFILE,
ENABLED,
DISALLOW_PROFILING,
+ REQUEST_FOREGROUND_SERVICE_EXEMPTION,
})
public @interface Values {}
private static final long EXTERNAL_STORAGE = 1L;
@@ -512,6 +513,7 @@
private static final long CROSS_PROFILE = 1L << 43;
private static final long ENABLED = 1L << 44;
private static final long DISALLOW_PROFILING = 1L << 45;
+ private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -2199,6 +2201,11 @@
}
@Override
+ public boolean hasRequestForegroundServiceExemption() {
+ return getBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION);
+ }
+
+ @Override
public ParsingPackageImpl setBaseRevisionCode(int value) {
baseRevisionCode = value;
return this;
@@ -2420,6 +2427,11 @@
}
@Override
+ public ParsingPackageImpl setRequestForegroundServiceExemption(boolean value) {
+ return setBoolean(Booleans.REQUEST_FOREGROUND_SERVICE_EXEMPTION, value);
+ }
+
+ @Override
public ParsingPackageImpl setUiOptions(int value) {
uiOptions = value;
return this;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 1c2c59f..35a2b9a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -906,10 +906,15 @@
*/
@ApplicationInfo.NativeHeapZeroInitialized
int getNativeHeapZeroInitialized();
-
@Nullable
Boolean hasRequestRawExternalStorageAccess();
+ /**
+ * @see ApplicationInfo#hasRequestForegroundServiceExemption()
+ * @see R.styleable#AndroidManifest_requestForegroundServiceExemption
+ */
+ boolean hasRequestForegroundServiceExemption();
+
// TODO(b/135203078): Hide and enforce going through PackageInfoUtils
ApplicationInfo toAppInfoWithoutState();
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 22d75ef..5d74e74 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -2033,6 +2033,12 @@
.AndroidManifestApplication_requestRawExternalStorageAccess,
false));
}
+ if (sa.hasValue(
+ R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
+ pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
+ .AndroidManifestApplication_requestForegroundServiceExemption,
+ false));
+ }
} finally {
sa.recycle();
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index d7b96df..f0d410f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1329,6 +1329,7 @@
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+ mWindow.getWindow().getAttributes().receiveInsetsIgnoringZOrder = true;
// Automotive devices may request the navigation bar to be hidden when the IME shows up
// (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 5b3bc26..38ed3fb 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -437,7 +437,8 @@
* Magic version number for a current development build, which has
* not yet turned into an official release.
*/
- public static final int CUR_DEVELOPMENT = VMRuntime.SDK_VERSION_CUR_DEVELOPMENT;
+ // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT.
+ public static final int CUR_DEVELOPMENT = 10000;
/**
* October 2008: The original, first, version of Android. Yay!
@@ -1324,6 +1325,7 @@
* selinux into "permissive" mode in particular.
* @hide
*/
+ @UnsupportedAppUsage
public static final boolean IS_DEBUGGABLE =
SystemProperties.getInt("ro.debuggable", 0) == 1;
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index b8bb353..d90e129 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -74,8 +74,9 @@
*
* @deprecated Accurate counting is a burden on the runtime and may be removed.
*/
+ // This must match VMDebug.TRACE_COUNT_ALLOCS.
@Deprecated
- public static final int TRACE_COUNT_ALLOCS = VMDebug.TRACE_COUNT_ALLOCS;
+ public static final int TRACE_COUNT_ALLOCS = 1;
/**
* Flags for printLoadedClasses(). Default behavior is to only show
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index 5f41174..473e7ae 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -16,9 +16,6 @@
package android.service.voice;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.CallSuper;
import android.annotation.DurationMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -33,10 +30,8 @@
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
-import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -67,6 +62,8 @@
* to inform the system that a keyphrase was not detected. The system then relays this result to
* the {@link VoiceInteractionService} through {@link HotwordDetector.Callback}.
*
+ * Note: Methods in this class may be called concurrently
+ *
* @hide
*/
@SystemApi
@@ -138,8 +135,6 @@
public static final String SERVICE_INTERFACE =
"android.service.voice.HotwordDetectionService";
- private Handler mHandler;
-
@Nullable
private ContentCaptureManager mContentCaptureManager;
@@ -154,13 +149,12 @@
if (DBG) {
Log.d(TAG, "#detectFromDspSource");
}
- mHandler.sendMessage(obtainMessage(HotwordDetectionService::onDetect,
- HotwordDetectionService.this,
+ HotwordDetectionService.this.onDetect(
new AlwaysOnHotwordDetector.EventPayload(
event.triggerInData, event.captureAvailable,
event.captureFormat, event.captureSession, event.data),
timeoutMillis,
- new Callback(callback)));
+ new Callback(callback));
}
@Override
@@ -169,11 +163,10 @@
if (DBG) {
Log.d(TAG, "#updateState");
}
- mHandler.sendMessage(obtainMessage(HotwordDetectionService::onUpdateStateInternal,
- HotwordDetectionService.this,
+ HotwordDetectionService.this.onUpdateStateInternal(
options,
sharedMemory,
- callback));
+ callback);
}
@Override
@@ -189,19 +182,15 @@
}
switch (audioSource) {
case AUDIO_SOURCE_MICROPHONE:
- mHandler.sendMessage(obtainMessage(
- HotwordDetectionService::onDetect,
- HotwordDetectionService.this,
- new Callback(callback)));
+ HotwordDetectionService.this.onDetect(
+ new Callback(callback));
break;
case AUDIO_SOURCE_EXTERNAL:
- mHandler.sendMessage(obtainMessage(
- HotwordDetectionService::onDetect,
- HotwordDetectionService.this,
+ HotwordDetectionService.this.onDetect(
audioStream,
audioFormat,
options,
- new Callback(callback)));
+ new Callback(callback));
break;
default:
Log.i(TAG, "Unsupported audio source " + audioSource);
@@ -216,13 +205,6 @@
}
};
- @CallSuper
- @Override
- public void onCreate() {
- super.onCreate();
- mHandler = Handler.createAsync(Looper.getMainLooper());
- }
-
@Override
@Nullable
public final IBinder onBind(@NonNull Intent intent) {
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 54c0ffc..80387aa 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.view.View;
import android.view.translation.TranslationResponseValue;
+import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
import android.widget.TextView;
@@ -67,7 +68,8 @@
Log.w(TAG, "Caller did not enable length changes; not transforming to translated text");
return source;
}
- TranslationResponseValue value = mTranslationResponse.getValue("text");
+ TranslationResponseValue value = mTranslationResponse.getValue(
+ ViewTranslationRequest.ID_TEXT);
CharSequence translatedText;
if (value.getStatusCode() == TranslationResponseValue.STATUS_SUCCESS) {
translatedText = value.getText();
diff --git a/core/java/android/view/translation/ViewTranslationRequest.java b/core/java/android/view/translation/ViewTranslationRequest.java
index 2913dfa..00f6ef2 100644
--- a/core/java/android/view/translation/ViewTranslationRequest.java
+++ b/core/java/android/view/translation/ViewTranslationRequest.java
@@ -43,7 +43,7 @@
* Constant id for the default view text to be translated. This is used by
* {@link Builder#setValue(String, TranslationRequestValue)}.
*/
- public static final String ID_TEXT = "text";
+ public static final String ID_TEXT = "android:text";
/**
* The {@link AutofillId} of the view associated with this request.
@@ -303,7 +303,7 @@
};
@DataClass.Generated(
- time = 1617119791798L,
+ time = 1620259482911L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/translation/ViewTranslationRequest.java",
inputSignatures = "public static final java.lang.String ID_TEXT\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\npublic @android.annotation.NonNull android.view.translation.TranslationRequestValue getValue(java.lang.String)\npublic @android.annotation.NonNull java.util.Set<java.lang.String> getKeys()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getAutofillId()\nprivate static java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nclass ViewTranslationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate @android.annotation.NonNull java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate long mBuilderFieldsSet\npublic @android.annotation.SuppressLint @android.annotation.NonNull android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\npublic @android.annotation.NonNull android.view.translation.ViewTranslationRequest build()\n android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\nprivate void checkNotUsed()\nclass Builder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genToString=true, genEqualsHashCode=true, genGetters=false, genHiddenConstructor=true)")
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 4d5b90a..4126801 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -401,8 +401,9 @@
int totalFramesCount = 0;
long maxFrameTimeNanos = 0;
+ int missedFramesCount = 0;
int missedAppFramesCount = 0;
- int missedSfFramesCounts = 0;
+ int missedSfFramesCount = 0;
for (int i = 0; i <= indexOnOrAfterEnd; i++) {
JankInfo info = mJankInfos.valueAt(i);
@@ -411,17 +412,23 @@
}
if (info.surfaceControlCallbackFired) {
totalFramesCount++;
+ boolean missedFrame = false;
if ((info.jankType & PREDICTION_ERROR) != 0
|| ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0)) {
Log.w(TAG, "Missed App frame:" + info.jankType);
missedAppFramesCount++;
+ missedFrame = true;
}
if ((info.jankType & DISPLAY_HAL) != 0
|| (info.jankType & JANK_SURFACEFLINGER_DEADLINE_MISSED) != 0
|| (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0
|| (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0) {
Log.w(TAG, "Missed SF frame:" + info.jankType);
- missedSfFramesCounts++;
+ missedSfFramesCount++;
+ missedFrame = true;
+ }
+ if (missedFrame) {
+ missedFramesCount++;
}
// TODO (b/174755489): Early latch currently gets fired way too often, so we have
// to ignore it for now.
@@ -438,10 +445,12 @@
}
// Log the frame stats as counters to make them easily accessible in traces.
+ Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedFrames",
+ missedFramesCount);
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedAppFrames",
missedAppFramesCount);
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#missedSfFrames",
- missedSfFramesCounts);
+ missedSfFramesCount);
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#totalFrames",
totalFramesCount);
Trace.traceCounter(Trace.TRACE_TAG_APP, mSession.getName() + "#maxFrameTimeMillis",
@@ -449,7 +458,7 @@
// Trigger perfetto if necessary.
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
- && missedAppFramesCount + missedSfFramesCounts >= mTraceThresholdMissedFrames;
+ && missedFramesCount >= mTraceThresholdMissedFrames;
boolean overFrameTimeThreshold = mTraceThresholdFrameTimeMillis != -1
&& maxFrameTimeNanos >= mTraceThresholdFrameTimeMillis * NANOS_IN_MILLISECOND;
if (overMissedFramesThreshold || overFrameTimeThreshold) {
@@ -460,9 +469,10 @@
FrameworkStatsLog.UI_INTERACTION_FRAME_INFO_REPORTED,
mSession.getStatsdInteractionType(),
totalFramesCount,
- missedAppFramesCount + missedSfFramesCounts,
+ missedFramesCount,
maxFrameTimeNanos,
- missedSfFramesCounts);
+ missedSfFramesCount,
+ missedAppFramesCount);
if (mListener != null) {
mListener.onCujEvents(mSession, ACTION_METRICS_LOGGED);
}
@@ -472,7 +482,8 @@
+ " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
+ " totalFrames=" + totalFramesCount
+ " missedAppFrames=" + missedAppFramesCount
- + " missedSfFrames=" + missedSfFramesCounts
+ + " missedSfFrames=" + missedSfFramesCount
+ + " missedFrames=" + missedFramesCount
+ " maxFrameTimeMillis=" + maxFrameTimeNanos / NANOS_IN_MILLISECOND);
}
}
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
index 980e12d..83e2f2b 100644
--- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -31,7 +31,7 @@
}
static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
- return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFd(env, javaFd));
}
static const JNINativeMethod gNetworkUtilMethods[] = {
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c5f4fd1..6386274 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1954,6 +1954,13 @@
See the <a href="{@docRoot}about/versions/12/backup-restore">Changes in backup and restore</a>
document for the format of the XML file.-->
<attr name="dataExtractionRules" format="reference"/>
+
+ <!-- @hide Request exemption from the foreground service restrictions introduced in S
+ (https://developer.android.com/about/versions/12/foreground-services)
+ Note the framework <b>ignores</b> this attribute at this time. Once apps target S or above,
+ there's no way to be exempted (without using a privileged permission).
+ -->
+ <attr name="requestForegroundServiceExemption" format="boolean" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 500a9da..f7a9930 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3099,6 +3099,8 @@
<public name="durationBetweenRequestsMillis" />
<public name="showInInputMethodPicker" />
<public name="effectColor" />
+ <!-- @hide @TestApi -->
+ <public name="requestForegroundServiceExemption" />
</staging-public-group>
<staging-public-group type="drawable" first-id="0x010800b5">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index 180cceb..7e5fd92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -113,20 +113,18 @@
/**
* Animator for OneHanded transition animation which supports both alpha and bounds animation.
- *
- * @param <T> Type of property to animate, either offset (float)
*/
// TODO: Refactoring to use SpringAnimation and DynamicAnimation instead of using ValueAnimator
// to implement One-Handed transition animation. (b/185129031)
- public abstract static class OneHandedTransitionAnimator<T> extends ValueAnimator implements
+ public abstract static class OneHandedTransitionAnimator extends ValueAnimator implements
ValueAnimator.AnimatorUpdateListener,
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
private final WindowContainerToken mToken;
- private T mStartValue;
- private T mEndValue;
- private T mCurrentValue;
+ private float mStartValue;
+ private float mEndValue;
+ private float mCurrentValue;
private final List<OneHandedAnimationCallback> mOneHandedAnimationCallbacks =
new ArrayList<>();
@@ -137,7 +135,7 @@
private @TransitionDirection int mTransitionDirection;
private OneHandedTransitionAnimator(WindowContainerToken token, SurfaceControl leash,
- T startValue, T endValue) {
+ float startValue, float endValue) {
mLeash = leash;
mToken = token;
mStartValue = startValue;
@@ -204,7 +202,7 @@
mSurfaceTransactionHelper = helper;
}
- OneHandedTransitionAnimator<T> addOneHandedAnimationCallback(
+ OneHandedTransitionAnimator addOneHandedAnimationCallback(
OneHandedAnimationCallback callback) {
mOneHandedAnimationCallbacks.add(callback);
return this;
@@ -223,27 +221,27 @@
return mTransitionDirection;
}
- OneHandedTransitionAnimator<T> setTransitionDirection(int direction) {
+ OneHandedTransitionAnimator setTransitionDirection(int direction) {
mTransitionDirection = direction;
return this;
}
- T getStartValue() {
+ float getStartValue() {
return mStartValue;
}
- T getEndValue() {
+ float getEndValue() {
return mEndValue;
}
- void setCurrentValue(T value) {
+ void setCurrentValue(float value) {
mCurrentValue = value;
}
/**
* Updates the {@link #mEndValue}.
*/
- void updateEndValue(T endValue) {
+ void updateEndValue(float endValue) {
mEndValue = endValue;
}
@@ -252,10 +250,10 @@
}
@VisibleForTesting
- static OneHandedTransitionAnimator<Float> ofYOffset(WindowContainerToken token,
+ static OneHandedTransitionAnimator ofYOffset(WindowContainerToken token,
SurfaceControl leash, float startValue, float endValue, Rect displayBounds) {
- return new OneHandedTransitionAnimator<Float>(token, leash, startValue, endValue) {
+ return new OneHandedTransitionAnimator(token, leash, startValue, endValue) {
private final Rect mTmpRect = new Rect(displayBounds);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 7e45f95..7d061fb 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -221,6 +221,16 @@
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
iter2->dynamic_ref_table->addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table->mAssignedPackageId);
+
+ // Add the alias resources to the dynamic reference table of every package group. Since
+ // staging aliases can only be defined by the framework package (which is not a shared
+ // library), the compile-time package id of the framework is the same across all packages
+ // that compile against the framework.
+ for (const auto& package : iter2->packages_) {
+ for (const auto& entry : package.loaded_package_->GetAliasResourceIdMap()) {
+ iter->dynamic_ref_table->addAlias(entry.first, entry.second);
+ }
+ }
}
}
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index cb620cc..d17c328 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -41,6 +41,7 @@
namespace android {
+constexpr const static int kFrameworkPackageId = 0x01;
constexpr const static int kAppPackageId = 0x7f;
namespace {
@@ -675,6 +676,42 @@
}
} break;
+ case RES_TABLE_STAGED_ALIAS_TYPE: {
+ if (loaded_package->package_id_ != kFrameworkPackageId) {
+ LOG(WARNING) << "Alias chunk ignored for non-framework package '"
+ << loaded_package->package_name_ << "'";
+ break;
+ }
+
+ std::unordered_set<uint32_t> finalized_ids;
+ const auto lib_alias = child_chunk.header<ResTable_staged_alias_header>();
+ if (!lib_alias) {
+ return {};
+ }
+ const auto entry_begin = child_chunk.data_ptr().convert<ResTable_staged_alias_entry>();
+ const auto entry_end = entry_begin + dtohl(lib_alias->count);
+ for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) {
+ if (!entry_iter) {
+ return {};
+ }
+ auto finalized_id = dtohl(entry_iter->finalizedResId);
+ if (!finalized_ids.insert(finalized_id).second) {
+ LOG(ERROR) << StringPrintf("Repeated finalized resource id '%08x' in staged aliases.",
+ finalized_id);
+ return {};
+ }
+
+ auto staged_id = dtohl(entry_iter->stagedResId);
+ auto [_, success] = loaded_package->alias_id_map_.insert(std::make_pair(staged_id,
+ finalized_id));
+ if (!success) {
+ LOG(ERROR) << StringPrintf("Repeated staged resource id '%08x' in staged aliases.",
+ staged_id);
+ return {};
+ }
+ }
+ } break;
+
default:
LOG(WARNING) << StringPrintf("Unknown chunk type '%02x'.", chunk.type());
break;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 30500ab..cae2d0b 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -7079,6 +7079,10 @@
mLookupTable[buildPackageId] = runtimePackageId;
}
+void DynamicRefTable::addAlias(uint32_t stagedId, uint32_t finalizedId) {
+ mAliasId[stagedId] = finalizedId;
+}
+
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
@@ -7088,8 +7092,16 @@
return NO_ERROR;
}
- if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
- // No lookup needs to be done, app package IDs are absolute.
+ auto alias_id = mAliasId.find(res);
+ if (alias_id != mAliasId.end()) {
+ // Rewrite the resource id to its alias resource id. Since the alias resource id is a
+ // compile-time id, it still needs to be resolved further.
+ res = alias_id->second;
+ }
+
+ if (packageId == SYS_PACKAGE_ID || (packageId == APP_PACKAGE_ID && !mAppAsLib)) {
+ // No lookup needs to be done, app and framework package IDs are absolute.
+ *resId = res;
return NO_ERROR;
}
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 3b222c5..9bbdede 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -17,6 +17,7 @@
#ifndef LOADEDARSC_H_
#define LOADEDARSC_H_
+#include <map>
#include <memory>
#include <set>
#include <vector>
@@ -171,51 +172,51 @@
incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset);
// Returns the string pool where type names are stored.
- inline const ResStringPool* GetTypeStringPool() const {
+ const ResStringPool* GetTypeStringPool() const {
return &type_string_pool_;
}
// Returns the string pool where the names of resource entries are stored.
- inline const ResStringPool* GetKeyStringPool() const {
+ const ResStringPool* GetKeyStringPool() const {
return &key_string_pool_;
}
- inline const std::string& GetPackageName() const {
+ const std::string& GetPackageName() const {
return package_name_;
}
- inline int GetPackageId() const {
+ int GetPackageId() const {
return package_id_;
}
// Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
- inline bool IsDynamic() const {
+ bool IsDynamic() const {
return (property_flags_ & PROPERTY_DYNAMIC) != 0;
}
// Returns true if this package is a Runtime Resource Overlay.
- inline bool IsOverlay() const {
+ bool IsOverlay() const {
return (property_flags_ & PROPERTY_OVERLAY) != 0;
}
// Returns true if this package originates from a system provided resource.
- inline bool IsSystem() const {
+ bool IsSystem() const {
return (property_flags_ & PROPERTY_SYSTEM) != 0;
}
// Returns true if this package is a custom loader and should behave like an overlay.
- inline bool IsCustomLoader() const {
+ bool IsCustomLoader() const {
return (property_flags_ & PROPERTY_LOADER) != 0;
}
- inline package_property_t GetPropertyFlags() const {
+ package_property_t GetPropertyFlags() const {
return property_flags_;
}
// Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
// package could have been assigned a different package ID than what this LoadedPackage was
// compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
- inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
+ const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
return dynamic_package_map_;
}
@@ -270,6 +271,10 @@
return overlayable_map_;
}
+ const std::map<uint32_t, uint32_t>& GetAliasResourceIdMap() const {
+ return alias_id_map_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -287,6 +292,7 @@
ByteBucketArray<uint32_t> resource_ids_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;
+ std::map<uint32_t, uint32_t> alias_id_map_;
// A map of overlayable name to actor
std::unordered_map<std::string, std::string> overlayable_map_;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 17c1404..3d66244 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -39,6 +39,7 @@
#include <android/configuration.h>
#include <array>
+#include <map>
#include <memory>
namespace android {
@@ -229,30 +230,31 @@
};
enum {
- RES_NULL_TYPE = 0x0000,
- RES_STRING_POOL_TYPE = 0x0001,
- RES_TABLE_TYPE = 0x0002,
- RES_XML_TYPE = 0x0003,
+ RES_NULL_TYPE = 0x0000,
+ RES_STRING_POOL_TYPE = 0x0001,
+ RES_TABLE_TYPE = 0x0002,
+ RES_XML_TYPE = 0x0003,
// Chunk types in RES_XML_TYPE
- RES_XML_FIRST_CHUNK_TYPE = 0x0100,
- RES_XML_START_NAMESPACE_TYPE= 0x0100,
- RES_XML_END_NAMESPACE_TYPE = 0x0101,
- RES_XML_START_ELEMENT_TYPE = 0x0102,
- RES_XML_END_ELEMENT_TYPE = 0x0103,
- RES_XML_CDATA_TYPE = 0x0104,
- RES_XML_LAST_CHUNK_TYPE = 0x017f,
+ RES_XML_FIRST_CHUNK_TYPE = 0x0100,
+ RES_XML_START_NAMESPACE_TYPE = 0x0100,
+ RES_XML_END_NAMESPACE_TYPE = 0x0101,
+ RES_XML_START_ELEMENT_TYPE = 0x0102,
+ RES_XML_END_ELEMENT_TYPE = 0x0103,
+ RES_XML_CDATA_TYPE = 0x0104,
+ RES_XML_LAST_CHUNK_TYPE = 0x017f,
// This contains a uint32_t array mapping strings in the string
// pool back to resource identifiers. It is optional.
- RES_XML_RESOURCE_MAP_TYPE = 0x0180,
+ RES_XML_RESOURCE_MAP_TYPE = 0x0180,
// Chunk types in RES_TABLE_TYPE
- RES_TABLE_PACKAGE_TYPE = 0x0200,
- RES_TABLE_TYPE_TYPE = 0x0201,
- RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
- RES_TABLE_LIBRARY_TYPE = 0x0203,
- RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
+ RES_TABLE_PACKAGE_TYPE = 0x0200,
+ RES_TABLE_TYPE_TYPE = 0x0201,
+ RES_TABLE_TYPE_SPEC_TYPE = 0x0202,
+ RES_TABLE_LIBRARY_TYPE = 0x0203,
+ RES_TABLE_OVERLAYABLE_TYPE = 0x0204,
RES_TABLE_OVERLAYABLE_POLICY_TYPE = 0x0205,
+ RES_TABLE_STAGED_ALIAS_TYPE = 0x0206,
};
/**
@@ -1639,6 +1641,29 @@
};
/**
+ * A map that allows rewriting staged (non-finalized) resource ids to their finalized counterparts.
+ */
+struct ResTable_staged_alias_header
+{
+ struct ResChunk_header header;
+
+ // The number of ResTable_staged_alias_entry that follow this header.
+ uint32_t count;
+};
+
+/**
+ * Maps the staged (non-finalized) resource id to its finalized resource id.
+ */
+struct ResTable_staged_alias_entry
+{
+ // The compile-time staged resource id to rewrite.
+ uint32_t stagedResId;
+
+ // The compile-time finalized resource id to which the staged resource id should be rewritten.
+ uint32_t finalizedResId;
+};
+
+/**
* Specifies the set of resources that are explicitly allowed to be overlaid by RROs.
*/
struct ResTable_overlayable_header
@@ -1751,6 +1776,8 @@
void addMapping(uint8_t buildPackageId, uint8_t runtimePackageId);
+ void addAlias(uint32_t stagedId, uint32_t finalizedId);
+
// Returns whether or not the value must be looked up.
bool requiresLookup(const Res_value* value) const;
@@ -1768,6 +1795,7 @@
uint8_t mLookupTable[256];
KeyedVector<String16, uint8_t> mEntries;
bool mAppAsLib;
+ std::map<uint32_t, uint32_t> mAliasId;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0212309..c6ab8a2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -583,6 +583,7 @@
"libhwui_defaults",
"android_graphics_apex",
"android_graphics_jni",
+ "linker_hugepage_aligned",
],
export_header_lib_headers: ["android_graphics_apex_headers"],
target: {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f2f9a26..14d4937 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -7068,15 +7068,7 @@
/**
* Set a certain surround format as enabled or not.
- * @param audioFormat a surround format, the value is one of
- * {@link AudioFormat#ENCODING_AC3}, {@link AudioFormat#ENCODING_E_AC3},
- * {@link AudioFormat#ENCODING_DTS}, {@link AudioFormat#ENCODING_DTS_HD},
- * {@link AudioFormat#ENCODING_AAC_LC}, {@link AudioFormat#ENCODING_DOLBY_TRUEHD},
- * {@link AudioFormat#ENCODING_E_AC3_JOC}. Once {@link AudioFormat#ENCODING_AAC_LC} is
- * set as enabled, {@link AudioFormat#ENCODING_AAC_LC},
- * {@link AudioFormat#ENCODING_AAC_HE_V1}, {@link AudioFormat#ENCODING_AAC_HE_V2},
- * {@link AudioFormat#ENCODING_AAC_ELD}, {@link AudioFormat#ENCODING_AAC_XHE} are
- * all enabled.
+ *
* @param enabled the required surround format state, true for enabled, false for disabled
* @return true if successful, otherwise false
*/
diff --git a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
index 9bf910b..7478b3e 100644
--- a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
+++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
@@ -64,7 +64,7 @@
filter_code,
};
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -74,7 +74,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -107,7 +107,7 @@
static jint android_net_utils_bindSocketToNetworkHandle(JNIEnv *env, jobject thiz, jobject javaFd,
jlong netHandle) {
- return android_setsocknetwork(netHandle, AFileDescriptor_getFD(env, javaFd));
+ return android_setsocknetwork(netHandle, AFileDescriptor_getFd(env, javaFd));
}
static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
@@ -156,7 +156,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
int rcode;
uint8_t buf[MAXPACKETSIZE] = {0};
@@ -182,7 +182,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
android_res_cancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -210,7 +210,7 @@
return NULL;
}
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
diff --git a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 0bafd5b..3d6cc68 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -2461,7 +2461,8 @@
* For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
* {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
* to be selected. This is logically different than
- * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+ * {@code NetworkCapabilities.NET_CAPABILITY_*}. Also note that multiple networks with the
+ * same transport type may be active concurrently.
*
* @param transportType the transport type to be added or removed.
* @return this builder
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 0210079..9c6113c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -86,7 +86,7 @@
private int mOriginatingUid = PackageInstaller.SessionParams.UID_UNKNOWN;
private String mOriginatingPackage; // The package name corresponding to #mOriginatingUid
- private boolean localLOGV = false;
+ private final boolean mLocalLOGV = false;
PackageManager mPm;
IPackageManager mIpm;
AppOpsManager mAppOpsManager;
@@ -104,7 +104,7 @@
private List<UnknownSourcesListener> mActiveUnknownSourcesListeners = new ArrayList<>(1);
// ApplicationInfo object primarily used for already existing applications
- private ApplicationInfo mAppInfo = null;
+ private ApplicationInfo mAppInfo;
// Buttons to indicate user acceptance
private Button mOk;
@@ -154,6 +154,7 @@
* @param id The dialog type to add
*/
private void showDialogInner(int id) {
+ if (mLocalLOGV) Log.i(TAG, "showDialogInner(" + id + ")");
DialogFragment currentDialog =
(DialogFragment) getFragmentManager().findFragmentByTag("dialog");
if (currentDialog != null) {
@@ -174,6 +175,7 @@
* @return The dialog
*/
private DialogFragment createDialog(int id) {
+ if (mLocalLOGV) Log.i(TAG, "createDialog(" + id + ")");
switch (id) {
case DLG_PACKAGE_ERROR:
return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
@@ -294,6 +296,7 @@
@Override
protected void onCreate(Bundle icicle) {
+ if (mLocalLOGV) Log.i(TAG, "creating for user " + getUserId());
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
@@ -354,6 +357,8 @@
}
boolean wasSetUp = processPackageUri(packageUri);
+ if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);
+
if (!wasSetUp) {
return;
}
@@ -363,6 +368,8 @@
protected void onResume() {
super.onResume();
+ if (mLocalLOGV) Log.i(TAG, "onResume(): mAppSnippet=" + mAppSnippet);
+
if (mAppSnippet != null) {
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
@@ -443,15 +450,21 @@
final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ if (mLocalLOGV) Log.i(TAG, "install not allowed: " + UserManager.DISALLOW_INSTALL_APPS);
showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
return;
} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ if (mLocalLOGV) {
+ Log.i(TAG, "install not allowed by admin; showing "
+ + Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ }
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
return;
}
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
+ if (mLocalLOGV) Log.i(TAG, "install allowed");
initiateInstall();
} else {
// Check for unknown sources restrictions.
@@ -462,6 +475,7 @@
final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
& (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
if (systemRestriction != 0) {
+ if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
@@ -475,13 +489,19 @@
}
private void startAdminSupportDetailsActivity(String restriction) {
+ if (mLocalLOGV) Log.i(TAG, "startAdminSupportDetailsActivity(): " + restriction);
+
// If the given restriction is set by an admin, display information about the
// admin enforcing the restriction for the affected user.
final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
final Intent showAdminSupportDetailsIntent = dpm.createAdminSupportIntent(restriction);
if (showAdminSupportDetailsIntent != null) {
+ if (mLocalLOGV) Log.i(TAG, "starting " + showAdminSupportDetailsIntent);
startActivity(showAdminSupportDetailsIntent);
+ } else {
+ if (mLocalLOGV) Log.w(TAG, "not intent for " + restriction);
}
+
finish();
}
@@ -497,6 +517,7 @@
final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpCode, mOriginatingUid,
mOriginatingPackage, mCallingAttributionTag,
"Started package installation activity");
+ if (mLocalLOGV) Log.i(TAG, "handleUnknownSources(): appMode=" + appOpMode);
switch (appOpMode) {
case AppOpsManager.MODE_DEFAULT:
mAppOpsManager.setMode(appOpCode, mOriginatingUid,
@@ -527,6 +548,7 @@
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
+ if (mLocalLOGV) Log.i(TAG, "processPackageUri(): uri=" + packageUri + ", scheme=" + scheme);
switch (scheme) {
case SCHEME_PACKAGE: {
@@ -543,7 +565,9 @@
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
- mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
+ CharSequence label = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
+ if (mLocalLOGV) Log.i(TAG, "creating snippet for " + label);
+ mAppSnippet = new PackageUtil.AppSnippet(label,
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
@@ -559,6 +583,7 @@
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
+ if (mLocalLOGV) Log.i(TAG, "creating snippet for local file " + sourceFile);
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
@@ -604,7 +629,7 @@
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
+ if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
finish();
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index d3a9f8f..f5570df 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -107,13 +107,18 @@
icon);
}
- static public class AppSnippet {
+ static final class AppSnippet {
@NonNull public CharSequence label;
@Nullable public Drawable icon;
public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
this.label = label;
this.icon = icon;
}
+
+ @Override
+ public String toString() {
+ return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
+ }
}
/**
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
index 8c20e02..ea033a3 100644
--- a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -22,8 +22,8 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
+ android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
+ android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingBottom="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -70,6 +70,7 @@
android:id="@+id/bottom_summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
android:visibility="gone"
android:ellipsize="marquee"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index b96a4f5..e8da28f 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -118,7 +118,7 @@
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Цуцлах"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"Хослуулснаар холбогдсон үед таны харилцагчид болон дуудлагын түүхэд хандах боломжтой."</string>
<string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай хослуулж чадсангүй."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Буруу PIN эсхүл дамжих түлхүүрээс шалтгаалан <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай хослуулж чадсангүй."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"Буруу ПИН эсхүл дамжих түлхүүрээс шалтгаалан <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай хослуулж чадсангүй."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>-тай холбоо барих боломжгүй."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Хослуулахаас <xliff:g id="DEVICE_NAME">%1$s</xliff:g> татгалзсан."</string>
<string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Компьютер"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index ca185e2..645248c 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -504,7 +504,7 @@
<string name="cancel" msgid="5665114069455378395">"रद्द गर्नुहोस्"</string>
<string name="okay" msgid="949938843324579502">"ठिक छ"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"अलार्म र रिमाइन्डरहरू"</string>
- <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिनुहोस्"</string>
+ <string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"अलार्म तथा रिमाइन्डर सेट गर्न दिइयोस्"</string>
<string name="alarms_and_reminders_title" msgid="8819933264635406032">"घडी तथा रिमाइन्डरहरू"</string>
<string name="alarms_and_reminders_footer_title" product="default" msgid="1122213569699233612">"यो एपलाई अलार्म सेट गर्न तथा अन्य कार्यको समयतालिका तोक्न दिनुहोस्। तपाईंले आफ्नो फोन नचलाएका बेला पनि यो एप प्रयोग गरिन सक्छ। यसले गर्दा थप ब्याट्री खपत हुन सक्छ। यो अनुमति नदिइएका खण्डमा यो एप राम्ररी नचल्न सक्छ र यो एपका अलार्म पनि तोकिएको समयमा बज्ने छैनन्।"</string>
<string name="alarms_and_reminders_footer_title" product="tablet" msgid="4596201244991057839">"यो एपलाई अलार्म सेट गर्न तथा अन्य कार्यको समयतालिका तोक्न दिनुहोस्। तपाईंले आफ्नो ट्याब्लेट नचलाएका बेला पनि यो एप प्रयोग गरिन सक्छ। यसले गर्दा थप ब्याट्री खपत हुन सक्छ। यो अनुमति नदिइएका खण्डमा यो एप राम्ररी नचल्न सक्छ र यो एपका अलार्म पनि तोकिएको समयमा बज्ने छैनन्।"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 08d87df..404299a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1147,8 +1147,8 @@
<string name="battery_info_status_charging_wireless">Charging wirelessly</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_discharging">Not charging</string>
- <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
- <string name="battery_info_status_not_charging">Plugged in, can\'t charge right now</string>
+ <!-- Battery Info screen. Value for a status item. A state which device is connected with any charger(e.g. USB, Adapter or Wireless) but not charging yet. Used for diagnostic info screens, precise translation isn't needed -->
+ <string name="battery_info_status_not_charging">Connected, not charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_full">Charged</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 941f47f..01d66e6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -34,8 +34,11 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -350,6 +353,9 @@
public static String keyToString(int key) {
return SettingsState.keyToString(key);
}
+ @ChangeId
+ @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S)
+ private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L;
@Override
public boolean onCreate() {
@@ -1950,6 +1956,25 @@
// Skip checking readable annotations for test_only apps
checkReadableAnnotation(settingsType, settingName);
}
+ /**
+ * some settings need additional permission check, this is to have a matching security
+ * control from other API alternatives returning the same settings values.
+ * note, the permission enforcement should be based on app's targetSDKlevel to better handle
+ * app-compat.
+ */
+ switch (settingName) {
+ // missing READ_PRIVILEGED_PHONE_STATE permission protection
+ // see alternative API {@link SubscriptionManager#getPreferredDataSubscriptionId()
+ case Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION:
+ // app-compat handling, not break apps targeting on previous SDKs.
+ if (CompatChanges.isChangeEnabled(
+ ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL)) {
+ getContext().enforceCallingOrSelfPermission(
+ Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ "access global settings MULTI_SIM_DATA_CALL_SUBSCRIPTION");
+ }
+ break;
+ }
if (!ai.isInstantApp()) {
return;
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c60ec78..2283844 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -151,7 +151,7 @@
<dimen name="notification_max_heads_up_height_before_s">162dp</dimen>
<!-- Height of a heads up notification in the status bar -->
- <dimen name="notification_max_heads_up_height">132dp</dimen>
+ <dimen name="notification_max_heads_up_height">143dp</dimen>
<!-- Height of a heads up notification in the status bar -->
<dimen name="notification_max_heads_up_height_increased">188dp</dimen>
@@ -1199,7 +1199,7 @@
0
</item>
<!-- Ending text size in sp of batteryLevel for wireless charging animation -->
- <item name="wireless_charging_anim_battery_level_text_size_end" format="float" type="dimen">24
+ <item name="wireless_charging_anim_battery_level_text_size_end" format="float" type="dimen">48
</item>
<!-- time until battery info is at full opacity-->
<integer name="wireless_charging_anim_opacity_offset">80</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 5f27400..5a50f0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -105,7 +105,7 @@
@NonNull private final DumpManager mDumpManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardViewMediator mKeyguardViewMediator;
- @NonNull private final Vibrator mVibrator;
+ @Nullable private final Vibrator mVibrator;
@NonNull private final Handler mMainHandler;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@@ -135,7 +135,8 @@
private boolean mScreenOn;
private Runnable mAodInterruptRunnable;
- private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
+ @VisibleForTesting
+ static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES =
new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -144,7 +145,8 @@
private final VibrationEffect mEffectTick = VibrationEffect.get(VibrationEffect.EFFECT_TICK);
private final VibrationEffect mEffectTextureTick =
VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
- private final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ @VisibleForTesting
+ final VibrationEffect mEffectClick = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
private final VibrationEffect mEffectHeavy =
VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
private final VibrationEffect mDoubleClick =
@@ -152,6 +154,9 @@
private final Runnable mAcquiredVibration = new Runnable() {
@Override
public void run() {
+ if (mVibrator == null) {
+ return;
+ }
String effect = Settings.Global.getString(mContext.getContentResolver(),
"udfps_acquired_type");
mVibrator.vibrate(getVibration(effect, mEffectTick), VIBRATION_SONIFICATION_ATTRIBUTES);
@@ -389,24 +394,28 @@
PowerManager.USER_ACTIVITY_EVENT_TOUCH, 0);
// TODO: this should eventually be removed after ux testing
- final ContentResolver contentResolver = mContext.getContentResolver();
- int startEnabled = Settings.Global.getInt(contentResolver,
- "udfps_start", 0);
- if (startEnabled > 0) {
- String startEffectSetting = Settings.Global.getString(
- contentResolver, "udfps_start_type");
- mVibrator.vibrate(getVibration(startEffectSetting, mEffectClick),
- VIBRATION_SONIFICATION_ATTRIBUTES);
+ if (mVibrator != null) {
+ final ContentResolver contentResolver =
+ mContext.getContentResolver();
+ int startEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_start", 1);
+ if (startEnabled > 0) {
+ String startEffectSetting = Settings.Global.getString(
+ contentResolver, "udfps_start_type");
+ mVibrator.vibrate(getVibration(startEffectSetting,
+ mEffectClick), VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
+
+ int acquiredEnabled = Settings.Global.getInt(contentResolver,
+ "udfps_acquired", 0);
+ if (acquiredEnabled > 0) {
+ int delay = Settings.Global.getInt(contentResolver,
+ "udfps_acquired_delay", 500);
+ mMainHandler.removeCallbacks(mAcquiredVibration);
+ mMainHandler.postDelayed(mAcquiredVibration, delay);
+ }
}
- int acquiredEnabled = Settings.Global.getInt(contentResolver,
- "udfps_acquired", 0);
- if (acquiredEnabled > 0) {
- int delay = Settings.Global.getInt(contentResolver,
- "udfps_acquired_delay", 500);
- mMainHandler.removeCallbacks(mAcquiredVibration);
- mMainHandler.postDelayed(mAcquiredVibration, delay);
- }
handled = true;
} else if (sinceLastLog >= MIN_TOUCH_LOG_INTERVAL) {
Log.v(TAG, "onTouch | finger move: " + touchInfo);
@@ -456,11 +465,12 @@
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
- @NonNull ScreenLifecycle screenLifecycle) {
+ @NonNull ScreenLifecycle screenLifecycle,
+ @Nullable Vibrator vibrator) {
mContext = context;
// TODO (b/185124905): inject main handler and vibrator once done prototyping
mMainHandler = new Handler(Looper.getMainLooper());
- mVibrator = context.getSystemService(Vibrator.class);
+ mVibrator = vibrator;
mInflater = inflater;
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 9d47bbb..f01ac68 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -34,7 +34,7 @@
*/
public class WirelessChargingAnimation {
- public static final long DURATION = 1133;
+ public static final long DURATION = 1500;
private static final String TAG = "WirelessChargingView";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index 38ffec1..0d3e2ae 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -20,6 +20,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -42,7 +43,9 @@
*/
public class WirelessChargingLayout extends FrameLayout {
public static final int UNKNOWN_BATTERY_LEVEL = -1;
- private static final long RIPPLE_ANIMATION_DURATION = 1133;
+ private static final long RIPPLE_ANIMATION_DURATION = 1500;
+ private static final int SCRIM_COLOR = 0x4C000000;
+ private static final int SCRIM_FADE_DURATION = 300;
private ChargingRippleView mRippleView;
public WirelessChargingLayout(Context context) {
@@ -121,6 +124,19 @@
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator);
+ ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this,
+ "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR);
+ scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION);
+ scrimFadeInAnimator.setInterpolator(Interpolators.LINEAR);
+ ValueAnimator scrimFadeOutAnimator = ObjectAnimator.ofArgb(this,
+ "backgroundColor", SCRIM_COLOR, Color.TRANSPARENT);
+ scrimFadeOutAnimator.setDuration(SCRIM_FADE_DURATION);
+ scrimFadeOutAnimator.setInterpolator(Interpolators.LINEAR);
+ scrimFadeOutAnimator.setStartDelay(RIPPLE_ANIMATION_DURATION - SCRIM_FADE_DURATION);
+ AnimatorSet animatorSetScrim = new AnimatorSet();
+ animatorSetScrim.playTogether(scrimFadeInAnimator, scrimFadeOutAnimator);
+ animatorSetScrim.start();
+
mRippleView = findViewById(R.id.wireless_charging_ripple);
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 04e32a1..3a6f1d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -190,6 +190,16 @@
|| vpnName != null || vpnNameWorkProfile != null
|| isProfileOwnerOfOrganizationOwnedDevice || isParentalControlsEnabled
|| (hasWorkProfile && isNetworkLoggingEnabled);
+ // Update the view to be untappable if the device is an organization-owned device with a
+ // managed profile and there is no policy set which requires a privacy disclosure.
+ if (mIsVisible && isProfileOwnerOfOrganizationOwnedDevice && !isNetworkLoggingEnabled
+ && !hasCACertsInWorkProfile && vpnNameWorkProfile == null) {
+ mRootView.setClickable(false);
+ mRootView.findViewById(R.id.footer_icon).setVisibility(View.GONE);
+ } else {
+ mRootView.setClickable(true);
+ mRootView.findViewById(R.id.footer_icon).setVisibility(View.VISIBLE);
+ }
// Update the string
mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
@@ -345,20 +355,15 @@
private View createOrganizationDialogView() {
final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
- boolean isProfileOwnerOfOrganizationOwnedDevice =
- mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
final CharSequence deviceOwnerOrganization =
mSecurityController.getDeviceOwnerOrganizationName();
- final CharSequence workProfileOrganizationName =
- mSecurityController.getWorkProfileOrganizationName();
final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
final String vpnName = mSecurityController.getPrimaryVpnName();
final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
-
View dialogView = LayoutInflater.from(mContext)
.inflate(R.layout.quick_settings_footer_dialog, null, false);
@@ -368,8 +373,7 @@
deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
- deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
- workProfileOrganizationName);
+ deviceOwnerOrganization);
if (managementMessage == null) {
dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
} else {
@@ -377,11 +381,7 @@
TextView deviceManagementWarning =
(TextView) dialogView.findViewById(R.id.device_management_warning);
deviceManagementWarning.setText(managementMessage);
- // Don't show the policies button for profile owner of org owned device, because there
- // is no policies settings screen for it
- if (!isProfileOwnerOfOrganizationOwnedDevice) {
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
- }
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
}
// ca certificate section
@@ -496,12 +496,11 @@
}
protected CharSequence getManagementMessage(boolean isDeviceManaged,
- CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
- CharSequence workProfileOrganizationName) {
- if (!isDeviceManaged && !isProfileOwnerOfOrganizationOwnedDevice) {
+ CharSequence organizationName) {
+ if (!isDeviceManaged) {
return null;
}
- if (isDeviceManaged && organizationName != null) {
+ if (organizationName != null) {
if (isFinancedDevice()) {
return mContext.getString(R.string.monitoring_financed_description_named_management,
organizationName, organizationName);
@@ -509,9 +508,6 @@
return mContext.getString(
R.string.monitoring_description_named_management, organizationName);
}
- } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
- return mContext.getString(
- R.string.monitoring_description_named_management, workProfileOrganizationName);
}
return mContext.getString(R.string.monitoring_description_management);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 875696a..2530cfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -35,6 +35,7 @@
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.Vibrator;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.LayoutInflater;
@@ -114,6 +115,8 @@
private AccessibilityManager mAccessibilityManager;
@Mock
private ScreenLifecycle mScreenLifecycle;
+ @Mock
+ private Vibrator mVibrator;
private FakeExecutor mFgExecutor;
@@ -170,7 +173,8 @@
mFalsingManager,
mPowerManager,
mAccessibilityManager,
- mScreenLifecycle);
+ mScreenLifecycle,
+ mVibrator);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -302,4 +306,29 @@
// THEN no illumination because screen is off
verify(mUdfpsView, never()).startIllumination(any());
}
+
+ @Test
+ public void playHapticOnTouchUdfpsArea() throws RemoteException {
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+
+ // GIVEN that the overlay is showing
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID,
+ IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mFgExecutor.runAllReady();
+
+ // WHEN ACTION_DOWN is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ downEvent.recycle();
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ moveEvent.recycle();
+
+ // THEN click haptic is played
+ verify(mVibrator).vibrate(mUdfpsController.mEffectClick,
+ UdfpsController.VIBRATION_SONIFICATION_ATTRIBUTES);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 234dec2..7caf0dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -21,6 +21,7 @@
import static junit.framework.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -189,6 +190,29 @@
}
@Test
+ public void testUntappableView_profileOwnerOfOrgOwnedDevice() {
+ when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
+
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+ assertFalse(mRootView.isClickable());
+ assertEquals(View.GONE, mRootView.findViewById(R.id.footer_icon).getVisibility());
+ }
+
+ @Test
+ public void testTappableView_profileOwnerOfOrgOwnedDevice_networkLoggingEnabled() {
+ when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
+ when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
+
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+ assertTrue(mRootView.isClickable());
+ assertEquals(View.VISIBLE, mRootView.findViewById(R.id.footer_icon).getVisibility());
+ }
+
+ @Test
public void testNetworkLoggingEnabled_deviceOwner() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
@@ -435,10 +459,7 @@
@Test
public void testGetManagementMessage_noManagement() {
assertEquals(null, mFooter.getManagementMessage(
- /* isDeviceManaged= */ false,
- MANAGING_ORGANIZATION,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
- MANAGING_ORGANIZATION));
+ /* isDeviceManaged= */ false, MANAGING_ORGANIZATION));
}
@Test
@@ -446,16 +467,11 @@
assertEquals(mContext.getString(R.string.monitoring_description_named_management,
MANAGING_ORGANIZATION),
mFooter.getManagementMessage(
- /* isDeviceManaged= */ true,
- MANAGING_ORGANIZATION,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
- /* workProfileOrganizationName= */ null));
+ /* isDeviceManaged= */ true, MANAGING_ORGANIZATION));
assertEquals(mContext.getString(R.string.monitoring_description_management),
mFooter.getManagementMessage(
/* isDeviceManaged= */ true,
- /* organizationName= */ null,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
- /* workProfileOrganizationName= */ null));
+ /* organizationName= */ null));
}
@Test
@@ -467,27 +483,7 @@
assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
MANAGING_ORGANIZATION, MANAGING_ORGANIZATION),
mFooter.getManagementMessage(
- /* isDeviceManaged= */ true,
- MANAGING_ORGANIZATION,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
- /* workProfileOrganizationName= */ null));
- }
-
- @Test
- public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
- assertEquals(mContext.getString(R.string.monitoring_description_named_management,
- MANAGING_ORGANIZATION),
- mFooter.getManagementMessage(
- /* isDeviceManaged= */ false,
- /* organizationName= */ null,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
- MANAGING_ORGANIZATION));
- assertEquals(mContext.getString(R.string.monitoring_description_management),
- mFooter.getManagementMessage(
- /* isDeviceManaged= */ false,
- /* organizationName= */ null,
- /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
- /* workProfileOrganizationName= */ null));
+ /* isDeviceManaged= */ true, MANAGING_ORGANIZATION));
}
@Test
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1bd53ae..aadb25c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -24,6 +24,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
+import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE;
@@ -5874,7 +5875,12 @@
ret = REASON_OP_ACTIVATE_PLATFORM_VPN;
}
}
-
+ if (ret == REASON_DENIED) {
+ if (mAm.mConstants.mFgsAllowOptOut
+ && targetService.appInfo.hasRequestForegroundServiceExemption()) {
+ ret = REASON_OPT_OUT_REQUESTED;
+ }
+ }
return ret;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0979585..0fff8be 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -110,6 +110,7 @@
static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
static final String KEY_KILL_FAS_CACHED_IDLE = "kill_fas_cached_idle";
+ static final String KEY_FGS_ALLOW_OPT_OUT = "fgs_allow_opt_out";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -161,6 +162,7 @@
*/
private static final int
DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR = 1;
+ private static final boolean DEFAULT_FGS_ALLOW_OPT_OUT = false;
// Flag stored in the DeviceConfig API.
/**
@@ -493,6 +495,12 @@
*/
volatile boolean mKillForceAppStandByAndCachedIdle = DEFAULT_KILL_FAS_CACHED_IDLE;
+ /**
+ * Whether to allow "opt-out" from the foreground service restrictions.
+ * (https://developer.android.com/about/versions/12/foreground-services)
+ */
+ volatile boolean mFgsAllowOptOut = DEFAULT_FGS_ALLOW_OPT_OUT;
+
private final ActivityManagerService mService;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -701,6 +709,9 @@
case KEY_KILL_FAS_CACHED_IDLE:
updateKillFasCachedIdle();
break;
+ case KEY_FGS_ALLOW_OPT_OUT:
+ updateFgsAllowOptOut();
+ break;
default:
break;
}
@@ -1040,6 +1051,13 @@
DEFAULT_KILL_FAS_CACHED_IDLE);
}
+ private void updateFgsAllowOptOut() {
+ mFgsAllowOptOut = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_FGS_ALLOW_OPT_OUT,
+ DEFAULT_FGS_ALLOW_OPT_OUT);
+ }
+
private void updateImperceptibleKillExemptions() {
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.clear();
IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES.addAll(mDefaultImperceptibleKillExemptPackages);
@@ -1260,6 +1278,8 @@
pw.print("="); pw.println(mFgsAtomSampleRate);
pw.print(" "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
+ pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
+ pw.print("="); pw.println(mFgsAllowOptOut);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 99b0d81..50515882 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -30,6 +30,7 @@
import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.AppOpsManager.NoteOpEvent;
@@ -827,6 +828,14 @@
@GuardedBy("AppOpsService.this")
private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
+ /**
+ * Currently paused startOp events
+ *
+ * <p>Key is clientId
+ */
+ @GuardedBy("AppOpsService.this")
+ private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
+
AttributedOp(@Nullable String tag, @NonNull Op parent) {
this.tag = tag;
this.parent = parent;
@@ -944,23 +953,36 @@
@Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
@AppOpsManager.UidState int uidState, @OpFlags int flags,
boolean triggerCallbackIfNeeded) throws RemoteException {
- if (triggerCallbackIfNeeded && !parent.isRunning()) {
+ startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, triggerCallbackIfNeeded, true);
+ }
+
+ private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags,
+ boolean triggerCallbackIfNeeded, boolean isStarted) throws RemoteException {
+ if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
parent.packageName, true);
}
- if (mInProgressEvents == null) {
+
+ if (isStarted && mInProgressEvents == null) {
mInProgressEvents = new ArrayMap<>(1);
+ } else if (mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
}
+ ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
+ ? mInProgressEvents : mPausedInProgressEvents;
long startTime = System.currentTimeMillis();
- InProgressStartOpEvent event = mInProgressEvents.get(clientId);
+ InProgressStartOpEvent event = events.get(clientId);
if (event == null) {
event = mInProgressStartOpEventPool.acquire(startTime,
SystemClock.elapsedRealtime(), clientId,
PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags);
- mInProgressEvents.put(clientId, event);
+ events.put(clientId, event);
} else {
if (uidState != event.mUidState) {
onUidStateChanged(uidState);
@@ -969,12 +991,15 @@
event.numUnfinishedStarts++;
- mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, startTime);
+ if (isStarted) {
+ mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags, startTime);
+ }
+
}
/**
- * Update state when finishOp was called
+ * Update state when finishOp was called. Will finish started ops, and delete paused ops.
*
* @param clientId Id of the finishOp caller
*/
@@ -983,22 +1008,32 @@
}
private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
- if (mInProgressEvents == null) {
- Slog.wtf(TAG, "No ops running");
- return;
- }
+ finishOrPause(clientId, triggerCallbackIfNeeded, false);
+ }
- int indexOfToken = mInProgressEvents.indexOfKey(clientId);
+ /**
+ * Update state when paused or finished is called. If pausing, it records the op as
+ * stopping in the HistoricalRegistry, but does not delete it.
+ */
+ private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
+ boolean isPausing) {
+ int indexOfToken = mInProgressEvents != null
+ ? mInProgressEvents.indexOfKey(clientId) : -1;
if (indexOfToken < 0) {
- Slog.wtf(TAG, "No op running for the client");
+ finishPossiblyPaused(clientId, isPausing);
return;
}
InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
- event.numUnfinishedStarts--;
- if (event.numUnfinishedStarts == 0) {
- event.finish();
- mInProgressEvents.removeAt(indexOfToken);
+ if (!isPausing) {
+ event.numUnfinishedStarts--;
+ }
+ // If we are pausing, create a NoteOpEvent, but don't change the InProgress event
+ if (event.numUnfinishedStarts == 0 || isPausing) {
+ if (!isPausing) {
+ event.finish();
+ mInProgressEvents.removeAt(indexOfToken);
+ }
if (mAccessEvents == null) {
mAccessEvents = new LongSparseArray<>(1);
@@ -1018,20 +1053,112 @@
parent.packageName, tag, event.getUidState(),
event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration());
- mInProgressStartOpEventPool.release(event);
+ if (!isPausing) {
+ mInProgressStartOpEventPool.release(event);
+ if (mInProgressEvents.isEmpty()) {
+ mInProgressEvents = null;
- if (mInProgressEvents.isEmpty()) {
- mInProgressEvents = null;
-
- // TODO moltmann: Also callback for single attribution tag activity changes
- if (triggerCallbackIfNeeded && !parent.isRunning()) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, false);
+ // TODO ntmyren: Also callback for single attribution tag activity changes
+ if (triggerCallbackIfNeeded && !parent.isRunning()) {
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, false);
+ }
}
}
}
}
+ // Finish or pause (no-op) an already paused op
+ private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
+ if (mPausedInProgressEvents == null) {
+ Slog.wtf(TAG, "No ops running or paused");
+ return;
+ }
+
+ int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId);
+ if (indexOfToken < 0) {
+ Slog.wtf(TAG, "No op running or paused for the client");
+ return;
+ } else if (isPausing) {
+ // already paused
+ return;
+ }
+
+ // no need to record a paused event finishing.
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
+ event.numUnfinishedStarts--;
+ if (event.numUnfinishedStarts == 0) {
+ mPausedInProgressEvents.removeAt(indexOfToken);
+ mInProgressStartOpEventPool.release(event);
+ if (mPausedInProgressEvents.isEmpty()) {
+ mPausedInProgressEvents = null;
+ }
+ }
+ }
+
+ /**
+ * Create an event that will be started, if the op is unpaused.
+ */
+ public void createPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @OpFlags int flags) throws RemoteException {
+ startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, true, false);
+ }
+
+ /**
+ * Pause all currently started ops. This will create a HistoricalRegistry
+ */
+ public void pause() {
+ if (mInProgressEvents == null) {
+ return;
+ }
+
+ if (mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
+ }
+
+ for (int i = 0; i < mInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+ mPausedInProgressEvents.put(event.mClientId, event);
+ finishOrPause(event.mClientId, true, true);
+ }
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, false);
+ mInProgressEvents = null;
+ }
+
+ /**
+ * Unpause all currently paused ops. This will reinitialize their start and duration
+ * times, but keep all other values the same
+ */
+ public void resume() {
+ if (mPausedInProgressEvents == null) {
+ return;
+ }
+
+ if (mInProgressEvents == null) {
+ mInProgressEvents = new ArrayMap<>(mPausedInProgressEvents.size());
+ }
+ boolean shouldSendActive = !mPausedInProgressEvents.isEmpty()
+ && mInProgressEvents.isEmpty();
+
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < mPausedInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
+ mInProgressEvents.put(event.mClientId, event);
+ event.mStartElapsedTime = SystemClock.elapsedRealtime();
+ event.mStartTime = startTime;
+ mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, event.mUidState, event.mFlags, startTime);
+ }
+ if (shouldSendActive) {
+ scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, true);
+ }
+ mPausedInProgressEvents = null;
+ }
+
/**
* Called in the case the client dies without calling finish first
*
@@ -2303,6 +2430,9 @@
scheduleWriteLocked();
}
uidState.evalForegroundOps(mOpModeWatchers);
+ if (mode != MODE_ERRORED && mode != previousMode) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
}
notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
@@ -2556,6 +2686,9 @@
pruneOpLocked(op, uid, packageName);
}
scheduleFastWriteLocked();
+ if (mode != MODE_ERRORED) {
+ updateStartedOpModeForUidLocked(code, mode == MODE_IGNORED, uid);
+ }
}
}
}
@@ -3660,6 +3793,7 @@
packageName);
}
+ boolean isRestricted = false;
synchronized (this) {
final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */);
if (ops == null) {
@@ -3673,18 +3807,10 @@
packageName);
}
final Op op = getOpLocked(ops, code, uid, true);
- if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
- if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
- flags, AppOpsManager.MODE_IGNORED);
- }
- return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
- packageName);
- }
-
final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
- final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
+ isRestricted = isOpRestrictedLocked(uid, code, packageName, bypass);
+ final int switchCode = AppOpsManager.opToSwitch(code);
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
@@ -3720,25 +3846,30 @@
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
+ + " package " + packageName + " restricted: " + isRestricted);
if (!dryRun) {
- scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
- AppOpsManager.MODE_ALLOWED);
try {
- attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState.state, flags);
+ if (isRestricted) {
+ attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.state, flags);
+ } else {
+ attributedOp.started(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState.state, flags);
+ }
} catch (RemoteException e) {
throw new RuntimeException(e);
}
+ scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags,
+ isRestricted ? MODE_IGNORED : MODE_ALLOWED);
}
}
- if (shouldCollectAsyncNotedOp && !dryRun) {
+ if (shouldCollectAsyncNotedOp && !dryRun && !isRestricted) {
collectAsyncNotedOp(uid, packageName, code, attributionTag, AppOpsManager.OP_FLAG_SELF,
message, shouldCollectMessage);
}
- return new SyncNotedAppOp(AppOpsManager.MODE_ALLOWED, code, attributionTag,
+ return new SyncNotedAppOp(isRestricted ? MODE_IGNORED : MODE_ALLOWED, code, attributionTag,
packageName);
}
@@ -6116,6 +6247,9 @@
if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
mHandler.sendMessage(PooledLambda.obtainMessage(
AppOpsService::notifyWatchersOfChange, this, code, UID_ANY));
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::updateStartedOpModeForUser, this, code, restricted,
+ userHandle));
}
if (restrictionState.isDefault()) {
@@ -6125,6 +6259,44 @@
}
}
+ private void updateStartedOpModeForUser(int code, boolean restricted, int userId) {
+ synchronized (AppOpsService.this) {
+ int numUids = mUidStates.size();
+ for (int uidNum = 0; uidNum < numUids; uidNum++) {
+ int uid = mUidStates.keyAt(uidNum);
+ if (userId != UserHandle.USER_ALL && UserHandle.getUserId(uid) != userId) {
+ continue;
+ }
+ updateStartedOpModeForUidLocked(code, restricted, uid);
+ }
+ }
+ }
+
+ private void updateStartedOpModeForUidLocked(int code, boolean restricted, int uid) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null || uidState.pkgOps == null) {
+ return;
+ }
+
+ int numPkgOps = uidState.pkgOps.size();
+ for (int pkgNum = 0; pkgNum < numPkgOps; pkgNum++) {
+ Ops ops = uidState.pkgOps.valueAt(pkgNum);
+ Op op = ops != null ? ops.get(code) : null;
+ if (op == null || (op.mode != MODE_ALLOWED && op.mode != MODE_FOREGROUND)) {
+ continue;
+ }
+ int numAttrTags = op.mAttributions.size();
+ for (int attrNum = 0; attrNum < numAttrTags; attrNum++) {
+ AttributedOp attrOp = op.mAttributions.valueAt(attrNum);
+ if (restricted) {
+ attrOp.pause();
+ } else {
+ attrOp.resume();
+ }
+ }
+ }
+ }
+
private void notifyWatchersOfChange(int code, int uid) {
final ArraySet<ModeCallback> clonedCallbacks;
synchronized (this) {
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5556015..35e8d34 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -555,8 +555,6 @@
}
if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_PASSIVE) {
mDiscreteRegistry.setDebugMode(true);
- } else {
- mDiscreteRegistry.setDebugMode(false);
}
}
if (mBaseSnapshotInterval != baseSnapshotInterval) {
@@ -627,6 +625,7 @@
}
setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS,
DEFAULT_COMPRESSION_STEP);
+ mDiscreteRegistry.setDebugMode(false);
}
void clearHistory(int uid, String packageName) {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 1261296..2c2a2bf 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -567,7 +567,11 @@
out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp);
out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
out.attributeInt(null, ATTR_USER, userSerialNo);
- out.attribute(null, ATTR_UNIQUE_DISPLAY_ID, toWrite[i].uniqueDisplayId);
+ String uniqueDisplayId = toWrite[i].uniqueDisplayId;
+ if (uniqueDisplayId == null) {
+ uniqueDisplayId = "";
+ }
+ out.attribute(null, ATTR_UNIQUE_DISPLAY_ID, uniqueDisplayId);
out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel);
out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index cb05f8f..fefe953 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -221,7 +221,7 @@
}
}
- private class Setting {
+ protected class Setting {
@NonNull private final Context mContext;
@NonNull private final @CecSettingName String mName;
private final boolean mUserConfigurable;
@@ -566,7 +566,7 @@
}
}
- private String retrieveValue(@NonNull Setting setting, @NonNull String defaultValue) {
+ protected String retrieveValue(@NonNull Setting setting, @NonNull String defaultValue) {
@Storage int storage = getStorage(setting);
String storageKey = getStorageKey(setting);
if (storage == STORAGE_SYSPROPS) {
@@ -582,7 +582,7 @@
return null;
}
- private void storeValue(@NonNull Setting setting, @NonNull String value) {
+ protected void storeValue(@NonNull Setting setting, @NonNull String value) {
@Storage int storage = getStorage(setting);
String storageKey = getStorageKey(setting);
if (storage == STORAGE_SYSPROPS) {
@@ -626,7 +626,7 @@
notifySettingChanged(setting);
}
- private void notifySettingChanged(@NonNull Setting setting) {
+ protected void notifySettingChanged(@NonNull Setting setting) {
synchronized (mLock) {
ArrayMap<SettingChangeListener, Executor> listeners =
mSettingChangeListeners.get(setting);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 172a68a..c55cf51 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -269,6 +269,13 @@
mInjector.getSettingsHelper().addOnLocationEnabledChangedListener(
this::onLocationModeChanged);
+ mInjector.getSettingsHelper().addOnIgnoreSettingsPackageWhitelistChangedListener(
+ () -> refreshAppOpsRestrictions(UserHandle.USER_ALL));
+ mInjector.getUserInfoHelper().addListener((userId, change) -> {
+ if (change == UserInfoHelper.UserListener.USER_STARTED) {
+ refreshAppOpsRestrictions(userId);
+ }
+ });
// set up passive provider first since it will be required for all other location providers,
// which are loaded later once the system is ready.
@@ -482,6 +489,8 @@
.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+
+ refreshAppOpsRestrictions(userId);
}
@Override
@@ -1347,6 +1356,43 @@
ipw.decreaseIndent();
}
+ private void refreshAppOpsRestrictions(int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ final int[] runningUserIds = mInjector.getUserInfoHelper().getRunningUserIds();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ refreshAppOpsRestrictions(runningUserIds[i]);
+ }
+ return;
+ }
+
+ Preconditions.checkArgument(userId >= 0);
+
+ ArraySet<String> packages = new ArraySet<>();
+ for (LocationProviderManager manager : mProviderManagers) {
+ packages.add(manager.getIdentity().getPackageName());
+ }
+ packages.add(mContext.getPackageName());
+ packages.addAll(mInjector.getSettingsHelper().getIgnoreSettingsPackageWhitelist());
+ String[] allowedPackages = packages.toArray(new String[0]);
+
+ boolean enabled = mInjector.getSettingsHelper().isLocationEnabled(userId);
+
+ AppOpsManager appOpsManager = Objects.requireNonNull(
+ mContext.getSystemService(AppOpsManager.class));
+ appOpsManager.setUserRestrictionForUser(
+ AppOpsManager.OP_COARSE_LOCATION,
+ enabled,
+ LocationManagerService.this,
+ allowedPackages,
+ userId);
+ appOpsManager.setUserRestrictionForUser(
+ AppOpsManager.OP_FINE_LOCATION,
+ enabled,
+ LocationManagerService.this,
+ allowedPackages,
+ userId);
+ }
+
private class LocalService extends LocationManagerInternal {
LocalService() {}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index af0aa76..77c1c1d 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
@@ -24,8 +23,11 @@
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.os.BatteryManagerInternal;
import android.os.Environment;
@@ -35,6 +37,7 @@
import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -63,9 +66,7 @@
private static final int JOB_IDLE_OPTIMIZE = 800;
private static final int JOB_POST_BOOT_UPDATE = 801;
- private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
- ? TimeUnit.MINUTES.toMillis(1)
- : TimeUnit.DAYS.toMillis(1);
+ private static final long IDLE_OPTIMIZATION_PERIOD = TimeUnit.DAYS.toMillis(1);
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
@@ -113,14 +114,24 @@
return;
}
- JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ final JobScheduler js = context.getSystemService(JobScheduler.class);
// Schedule a one-off job which scans installed packages and updates
- // out-of-date oat files.
- js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
- .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
- .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
- .build());
+ // out-of-date oat files. Schedule it 10 minutes after the boot complete event,
+ // so that we don't overload the boot with additional dex2oat compilations.
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
+ .build());
+ context.unregisterReceiver(this);
+ if (DEBUG) {
+ Slog.i(TAG, "BootBgDexopt scheduled");
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
// Schedule a daily job which scans installed packages and compiles
// those with fresh profiling data.
@@ -130,8 +141,8 @@
.setPeriodic(IDLE_OPTIMIZATION_PERIOD)
.build());
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Jobs scheduled");
+ if (DEBUG) {
+ Slog.d(TAG, "BgDexopt scheduled");
}
}
@@ -151,7 +162,7 @@
@SuppressWarnings("deprecation")
final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
if (lowThreshold == 0) {
- Log.e(TAG, "Invalid low storage threshold");
+ Slog.e(TAG, "Invalid low storage threshold");
}
return lowThreshold;
@@ -198,13 +209,12 @@
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " +
usableSpace);
break;
}
-
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Updating package " + pkg);
+ if (DEBUG) {
+ Slog.i(TAG, "Updating package " + pkg);
}
// Update package if needed. Note that there can be no race between concurrent
@@ -236,13 +246,13 @@
public void run() {
int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
if (result == OPTIMIZE_PROCESSED) {
- Log.i(TAG, "Idle optimizations completed.");
+ Slog.i(TAG, "Idle optimizations completed.");
} else if (result == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
- Log.w(TAG, "Idle optimizations aborted because of space constraints.");
+ Slog.w(TAG, "Idle optimizations aborted because of space constraints.");
} else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- Log.w(TAG, "Idle optimizations aborted by job scheduler.");
+ Slog.w(TAG, "Idle optimizations aborted by job scheduler.");
} else {
- Log.w(TAG, "Idle optimizations ended with unexpected code: " + result);
+ Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result);
}
if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
// Abandon our timeslice and do not reschedule.
@@ -256,7 +266,7 @@
// Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
Context context) {
- Log.i(TAG, "Performing idle optimizations");
+ Slog.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
mAbortIdleOptimization.set(false);
@@ -331,11 +341,15 @@
final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
* lowStorageThreshold;
boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
- Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+ if (DEBUG) {
+ Slog.d(TAG, "Should Downgrade " + shouldDowngrade);
+ }
if (shouldDowngrade) {
Set<String> unusedPackages =
pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
- Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ if (DEBUG) {
+ Slog.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ }
if (!unusedPackages.isEmpty()) {
for (String pkg : unusedPackages) {
@@ -407,7 +421,9 @@
*/
private boolean downgradePackage(PackageManagerService pm, String pkg,
boolean isForPrimaryDex) {
- Log.d(TAG, "Downgrading " + pkg);
+ if (DEBUG) {
+ Slog.d(TAG, "Downgrading " + pkg);
+ }
boolean dex_opt_performed = false;
int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -529,7 +545,7 @@
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowStorageThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
return OPTIMIZE_ABORT_NO_SPACE_LEFT;
}
@@ -568,8 +584,8 @@
@Override
public boolean onStartJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStartJob");
+ if (DEBUG) {
+ Slog.i(TAG, "onStartJob");
}
// NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
@@ -577,17 +593,13 @@
// restart with a period of ~1 minute.
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
if (pm.isStorageLow()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Low storage, skipping this run");
- }
+ Slog.i(TAG, "Low storage, skipping this run");
return false;
}
final ArraySet<String> pkgs = pm.getOptimizablePackages();
if (pkgs.isEmpty()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "No packages to optimize");
- }
+ Slog.i(TAG, "No packages to optimize");
return false;
}
@@ -603,8 +615,8 @@
@Override
public boolean onStopJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStopJob");
+ if (DEBUG) {
+ Slog.d(TAG, "onStopJob");
}
if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
@@ -625,7 +637,7 @@
private void notifyPinService(ArraySet<String> updatedPackages) {
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
if (pinnerService != null) {
- Log.i(TAG, "Pinning optimized code " + updatedPackages);
+ Slog.i(TAG, "Pinning optimized code " + updatedPackages);
pinnerService.update(updatedPackages, false /* force */);
}
}
@@ -660,7 +672,7 @@
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
if (sysPropValue == null || sysPropValue.isEmpty()) {
- Log.w(TAG, "SysProp " + sysPropKey + " not set");
+ Slog.w(TAG, "SysProp " + sysPropKey + " not set");
return Long.MAX_VALUE;
}
return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8f3702a..9f51d97 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -115,8 +115,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -325,7 +323,6 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
@@ -2385,7 +2382,8 @@
return occludesParent(false /* includingFinishing */);
}
- private boolean occludesParent(boolean includingFinishing) {
+ @VisibleForTesting
+ boolean occludesParent(boolean includingFinishing) {
if (!includingFinishing && finishing) {
return false;
}
@@ -2843,7 +2841,6 @@
final boolean endTask = task.getTopNonFinishingActivity() == null
&& !task.isClearingToReuseTask();
- final int transit = endTask ? TRANSIT_OLD_TASK_CLOSE : TRANSIT_OLD_ACTIVITY_CLOSE;
if (newTransition != null) {
mAtmService.getTransitionController().requestStartTransition(newTransition,
endTask ? task : null, null /* remote */);
@@ -2900,7 +2897,7 @@
} else if (!isState(PAUSING)) {
if (mVisibleRequested) {
// Prepare and execute close transition.
- prepareActivityHideTransitionAnimation(transit);
+ prepareActivityHideTransitionAnimation();
}
final boolean removedActivity = completeFinishing("finishIfPossible") == null;
@@ -2918,11 +2915,9 @@
// In this case, we can set the visibility of all the task overlay activities when
// we detect the last one is finishing to keep them in sync.
if (task.onlyHasTaskOverlayActivities(false /* includeFinishing */)) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityRecord::prepareActivityHideTransitionAnimationIfOvarlay,
- PooledLambda.__(ActivityRecord.class), transit);
- task.forAllActivities(c);
- c.recycle();
+ task.forAllActivities((r) -> {
+ r.prepareActivityHideTransitionAnimationIfOvarlay();
+ });
}
return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
} else {
@@ -2935,28 +2930,33 @@
}
}
- private void prepareActivityHideTransitionAnimationIfOvarlay(@TransitionOldType int transit) {
+ private void prepareActivityHideTransitionAnimationIfOvarlay() {
if (mTaskOverlay) {
- prepareActivityHideTransitionAnimation(transit);
+ prepareActivityHideTransitionAnimation();
}
}
- private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) {
+ private void prepareActivityHideTransitionAnimation() {
final DisplayContent dc = mDisplayContent;
dc.prepareAppTransition(TRANSIT_CLOSE);
setVisibility(false);
dc.executeAppTransition();
}
+ ActivityRecord completeFinishing(String reason) {
+ return completeFinishing(true /* updateVisibility */, reason);
+ }
+
/**
* Complete activity finish request that was initiated earlier. If the activity is still
* pausing we will wait for it to complete its transition. If the activity that should appear in
* place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
* destroyed right away.
+ * @param updateVisibility Indicate if need to update activity visibility.
* @param reason Reason for finishing the activity.
* @return Flag indicating whether the activity was removed from history.
*/
- ActivityRecord completeFinishing(String reason) {
+ ActivityRecord completeFinishing(boolean updateVisibility, String reason) {
if (!finishing || isState(RESUMED)) {
throw new IllegalArgumentException(
"Activity must be finishing and not resumed to complete, r=" + this
@@ -2968,13 +2968,11 @@
return this;
}
- final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED);
- if (isCurrentVisible) {
- final Task rootTask = getRootTask();
- final ActivityRecord activity = rootTask.getResumedActivity();
+ final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED, STARTED);
+ if (updateVisibility && isCurrentVisible) {
boolean ensureVisibility = false;
- if (activity != null && !activity.occludesParent()) {
- // If the resume activity is not opaque, we need to make sure the visibilities of
+ if (occludesParent(true /* includingFinishing */)) {
+ // If the current activity is not opaque, we need to make sure the visibilities of
// activities be updated, they may be seen by users.
ensureVisibility = true;
} else if (mTaskSupervisor.getKeyguardController().isKeyguardLocked()
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 411059d..206f67e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2449,6 +2449,10 @@
setWindowingMode(windowingMode);
}
+ /**
+ * See {@code WindowState#applyImeWindowsIfNeeded} for the details that we won't traverse the
+ * IME window in some cases.
+ */
boolean forAllImeWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
return mImeWindowsContainer.forAllWindowForce(callback, traverseTopToBottom);
}
@@ -4566,6 +4570,8 @@
private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
// We skip IME windows so they're processed just above their target, except
// in split-screen mode where we process the IME containers above the docked divider.
+ // Note that this method check should align with {@link
+ // WindowState#applyImeWindowsIfNeeded} in case of any state mismatch.
return dc.getImeTarget(IME_TARGET_LAYERING) != null
&& !dc.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e5e1a7a..be01173 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5842,8 +5842,13 @@
final boolean wasStopping = prev.isState(STOPPING);
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) {
+ // We will update the activity visibility later, no need to do in
+ // completeFinishing(). Updating visibility here might also making the next
+ // activities to be resumed, and could result in wrong app transition due to
+ // lack of previous activity information.
ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
- prev = prev.completeFinishing("completePausedLocked");
+ prev = prev.completeFinishing(false /* updateVisibility */,
+ "completePausedLocked");
} else if (prev.hasProcess()) {
ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ "wasStopping=%b visibleRequested=%b", prev, wasStopping,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b40223f..540035f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4842,7 +4842,10 @@
// directly above it. The exception is if we are in split screen
// in which case we process the IME at the DisplayContent level to
// ensure it is above the docked divider.
- if (isImeLayeringTarget() && !inSplitScreenWindowingMode()) {
+ // (i.e. Like {@link DisplayContent.ImeContainer#skipImeWindowsDuringTraversal}, the IME
+ // window will be ignored to traverse when the IME target is still in split-screen mode).
+ if (isImeLayeringTarget()
+ && !getDisplayContent().getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
if (getDisplayContent().forAllImeWindows(callback, traverseTopToBottom)) {
return true;
}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 6721ecf..bfd8005 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -66,7 +66,7 @@
android::sp<IStats> statsHal = new StatsHal();
const android::status_t err = statsHal->registerAsService();
- LOG_ALWAYS_FATAL_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
+ ALOGW_IF(err != android::OK, "Cannot register HIDL %s: %d", IStats::descriptor, err);
}
} // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ef7360d..23a67b7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -28,6 +28,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_USER;
+import static android.app.admin.DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED;
import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
import static android.app.admin.DevicePolicyManager.CODE_DEVICE_ADMIN_NOT_SUPPORTED;
@@ -883,12 +884,6 @@
synchronized (getLockObject()) {
// Check whether the user is affiliated, *before* removing its data.
boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
- if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
- // Disable network and security logging
- mInjector.securityLogSetLoggingEnabledProperty(false);
- mSecurityLogMonitor.stop();
- setNetworkLoggingActiveInternal(false);
- }
removeUserData(userHandle);
if (!isRemovedUserAffiliated) {
// We discard the logs when unaffiliated users are deleted (so that the
@@ -1779,6 +1774,7 @@
}
void removeUserData(int userHandle) {
+ final boolean isOrgOwned;
synchronized (getLockObject()) {
if (userHandle == UserHandle.USER_SYSTEM) {
Slogf.w(LOG_TAG, "Tried to remove device policy file for user 0! Ignoring.");
@@ -1786,6 +1782,9 @@
}
updatePasswordQualityCacheForUserGroup(userHandle);
mPolicyCache.onUserRemoved(userHandle);
+
+ isOrgOwned = mOwners.isProfileOwnerOfOrganizationOwnedDevice(userHandle);
+
mOwners.removeProfileOwner(userHandle);
mOwners.writeProfileOwner(userHandle);
@@ -1799,6 +1798,14 @@
policyFile.delete();
Slogf.i(LOG_TAG, "Removed device policy file " + policyFile.getAbsolutePath());
}
+ if (isOrgOwned) {
+ final UserInfo primaryUser = mUserManager.getPrimaryUser();
+ if (primaryUser != null) {
+ clearOrgOwnedProfileOwnerDeviceWidePolicies(primaryUser.id);
+ } else {
+ Slogf.wtf(LOG_TAG, "Was unable to get primary user.");
+ }
+ }
}
/**
@@ -3537,6 +3544,7 @@
"Caller must be shell or hold MANAGE_PROFILE_AND_DEVICE_OWNERS to call "
+ "forceRemoveActiveAdmin");
mInjector.binderWithCleanCallingIdentity(() -> {
+ boolean isOrgOwnedProfile = false;
synchronized (getLockObject()) {
if (!isAdminTestOnlyLocked(adminReceiver, userHandle)) {
throw new SecurityException("Attempt to remove non-test admin "
@@ -3548,13 +3556,7 @@
clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
}
if (isProfileOwner(adminReceiver, userHandle)) {
- if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
- UserHandle parentUserHandle = UserHandle.of(getProfileParentId(userHandle));
- mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
- false, parentUserHandle);
- mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER,
- false, parentUserHandle);
- }
+ isOrgOwnedProfile = isProfileOwnerOfOrganizationOwnedDevice(userHandle);
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
userHandle, /* parent */ false);
clearProfileOwnerLocked(admin, userHandle);
@@ -3562,11 +3564,26 @@
}
// Remove the admin skipping sending the broadcast.
removeAdminArtifacts(adminReceiver, userHandle);
+
+ // In case of PO on org owned device, clean device-wide policies and restrictions.
+ if (isOrgOwnedProfile) {
+ final UserHandle parentUser = UserHandle.of(getProfileParentId(userHandle));
+ clearOrgOwnedProfileOwnerUserRestrictions(parentUser);
+ clearOrgOwnedProfileOwnerDeviceWidePolicies(parentUser.getIdentifier());
+ }
+
Slogf.i(LOG_TAG, "Admin " + adminReceiver + " removed from user " + userHandle);
});
}
- private void clearDeviceOwnerUserRestrictionLocked(UserHandle userHandle) {
+ private void clearOrgOwnedProfileOwnerUserRestrictions(UserHandle parentUserHandle) {
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, parentUserHandle);
+ mUserManager.setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, parentUserHandle);
+ }
+
+ private void clearDeviceOwnerUserRestriction(UserHandle userHandle) {
// ManagedProvisioning/DPC sets DISALLOW_ADD_USER. Clear to recover to the original state
if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userHandle);
@@ -6710,25 +6727,16 @@
// when wipeData is _not_ called on the parent instance, it implies relinquishing
// control over the device, wiping only the work profile. So the user restriction
// on profile removal needs to be removed first.
-
- mInjector.binderWithCleanCallingIdentity(() -> {
- // Clear restriction as user.
- mUserManager.setUserRestriction(
- UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false,
- UserHandle.SYSTEM);
- mUserManager.setUserRestriction(
- UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
-
- // Device-wide policies set by the profile owner need to be cleaned up here.
- mLockPatternUtils.setDeviceOwnerInfo(null);
- });
+ final UserHandle parentUser = UserHandle.of(getProfileParentId(userId));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> clearOrgOwnedProfileOwnerUserRestrictions(parentUser));
}
}
DevicePolicyEventLogger event = DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
.setInt(flags)
- .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
- ;
+ .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT);
+
final String adminName;
final ComponentName adminComp;
if (admin != null) {
@@ -6755,6 +6763,55 @@
wipeDataNoLock(adminComp, flags, internalReason, wipeReasonForUser, userId);
}
+ /**
+ * Clears device wide policies enforced by COPE PO when relinquishing the device. This method
+ * should be invoked once the admin is gone, so that all methods that rely on calculating
+ * aggregate policy (e.g. strong auth timeout) from all admins aren't affected by its policies.
+ * This method assumes that there is no other device or profile owners left on the device.
+ * Shouldn't be called from binder thread without clearing identity.
+ */
+ private void clearOrgOwnedProfileOwnerDeviceWidePolicies(@UserIdInt int parentId) {
+ Slogf.i(LOG_TAG, "Cleaning up device-wide policies left over from org-owned profile...");
+ // Lockscreen message
+ mLockPatternUtils.setDeviceOwnerInfo(null);
+ // Wifi config lockdown
+ mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0);
+ // Security logging
+ if (mInjector.securityLogGetLoggingEnabledProperty()) {
+ mSecurityLogMonitor.stop();
+ mInjector.securityLogSetLoggingEnabledProperty(false);
+ }
+ // Network logging
+ setNetworkLoggingActiveInternal(false);
+
+ // System update policy.
+ final boolean hasSystemUpdatePolicy;
+ synchronized (getLockObject()) {
+ hasSystemUpdatePolicy = mOwners.getSystemUpdatePolicy() != null;
+ if (hasSystemUpdatePolicy) {
+ mOwners.clearSystemUpdatePolicy();
+ mOwners.writeDeviceOwner();
+ }
+ }
+ if (hasSystemUpdatePolicy) {
+ mContext.sendBroadcastAsUser(
+ new Intent(ACTION_SYSTEM_UPDATE_POLICY_CHANGED), UserHandle.SYSTEM);
+ }
+
+ // Unsuspend personal apps if needed.
+ suspendPersonalAppsInternal(parentId, false);
+
+ // Notify FRP agent, LSS and WindowManager to ensure they don't hold on to stale policies.
+ final int frpAgentUid = getFrpManagementAgentUid();
+ if (frpAgentUid > 0) {
+ notifyResetProtectionPolicyChanged(frpAgentUid);
+ }
+ mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
+ updateScreenCaptureDisabled(parentId, getScreenCaptureDisabled(null, parentId, false));
+
+ Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
+ }
+
private void wipeDataNoLock(ComponentName admin, int flags, String internalReason,
String wipeReasonForUser, int userId) {
wtfIfInLock();
@@ -6822,13 +6879,8 @@
saveSettingsLocked(caller.getUserId());
}
- final Intent intent = new Intent(
- DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED).addFlags(
- Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
-
- mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(intent,
- UserHandle.getUserHandleForUid(frpManagementAgentUid),
- android.Manifest.permission.MANAGE_FACTORY_RESET_PROTECTION));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> notifyResetProtectionPolicyChanged(frpManagementAgentUid));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_FACTORY_RESET_PROTECTION)
@@ -6836,6 +6888,16 @@
.write();
}
+ // Shouldn't be called from binder thread without clearing identity.
+ private void notifyResetProtectionPolicyChanged(int frpManagementAgentUid) {
+ final Intent intent = new Intent(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED).addFlags(
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent,
+ UserHandle.getUserHandleForUid(frpManagementAgentUid),
+ permission.MANAGE_FACTORY_RESET_PROTECTION);
+ }
+
@Override
public FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(
@Nullable ComponentName who) {
@@ -8506,7 +8568,7 @@
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
- clearDeviceOwnerUserRestrictionLocked(UserHandle.of(userId));
+ clearDeviceOwnerUserRestriction(UserHandle.of(userId));
mInjector.securityLogSetLoggingEnabledProperty(false);
mSecurityLogMonitor.stop();
setNetworkLoggingActiveInternal(false);
@@ -12936,8 +12998,7 @@
mOwners.writeDeviceOwner();
}
mInjector.binderWithCleanCallingIdentity(() -> mContext.sendBroadcastAsUser(
- new Intent(DevicePolicyManager.ACTION_SYSTEM_UPDATE_POLICY_CHANGED),
- UserHandle.SYSTEM));
+ new Intent(ACTION_SYSTEM_UPDATE_POLICY_CHANGED), UserHandle.SYSTEM));
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_SYSTEM_UPDATE_POLICY)
.setAdmin(who)
@@ -14513,14 +14574,10 @@
* apps.
*/
@Override
- public void forceUpdateUserSetupComplete() {
- final CallerIdentity caller = getCallerIdentity();
+ public void forceUpdateUserSetupComplete(@UserIdInt int userId) {
Preconditions.checkCallAuthorization(
hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
- Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(),
- "Caller has to be in user 0");
- final int userId = UserHandle.USER_SYSTEM;
boolean isUserCompleted = mInjector.settingsSecureGetIntForUser(
Settings.Secure.USER_SETUP_COMPLETE, 0, userId) != 0;
DevicePolicyData policy = getUserData(userId);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c54dffc..2270943 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -41,6 +41,7 @@
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_PROFILE_OFF_DEADLINE;
import static com.android.server.devicepolicy.DevicePolicyManagerService.ACTION_TURN_PROFILE_ON_NOTIFICATION;
import static com.android.server.devicepolicy.DpmMockContext.CALLER_USER_HANDLE;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static com.android.server.testutils.TestUtils.assertExpectException;
import static com.google.common.truth.Truth.assertThat;
@@ -82,6 +83,7 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.FactoryResetProtectionPolicy;
import android.app.admin.PasswordMetrics;
+import android.app.admin.SystemUpdatePolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -3888,37 +3890,28 @@
public void testForceUpdateUserSetupComplete_permission() {
// GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted
assertExpectException(SecurityException.class, /* messageRegex =*/ null,
- () -> dpm.forceUpdateUserSetupComplete());
- }
-
- @Test
- public void testForceUpdateUserSetupComplete_systemUser() {
- mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- // GIVEN calling from user 20
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
- assertExpectException(SecurityException.class, /* messageRegex =*/ null,
- () -> dpm.forceUpdateUserSetupComplete());
+ () -> dpm.forceUpdateUserSetupComplete(UserHandle.USER_SYSTEM));
}
@Test
public void testForceUpdateUserSetupComplete_forcesUpdate() {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ final int userId = UserHandle.getUserId(mContext.binder.callingUid);
- final int userId = UserHandle.USER_SYSTEM;
// GIVEN userComplete is false in SettingsProvider
setUserSetupCompleteForUser(false, userId);
// GIVEN userComplete is true in DPM
DevicePolicyData userData = new DevicePolicyData(userId);
userData.mUserSetupComplete = true;
- dpms.mUserData.put(UserHandle.USER_SYSTEM, userData);
+ dpms.mUserData.put(userId, userData);
assertThat(dpms.hasUserSetupCompleted()).isTrue();
- dpm.forceUpdateUserSetupComplete();
+ dpm.forceUpdateUserSetupComplete(userId);
- // THEN the state in dpms is not changed
+ // THEN the state in dpms is changed
assertThat(dpms.hasUserSetupCompleted()).isFalse();
}
@@ -3948,10 +3941,9 @@
// Enabling logging should not change the timestamp.
dpm.setSecurityLoggingEnabled(admin1, true);
- verify(getServices().settings)
- .securityLogSetLoggingEnabledProperty(true);
- when(getServices().settings.securityLogGetLoggingEnabledProperty())
- .thenReturn(true);
+ verify(getServices().settings).securityLogSetLoggingEnabledProperty(true);
+
+ when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
assertThat(dpm.getLastSecurityLogRetrievalTime()).isEqualTo(-1);
// Retrieving the logs should update the timestamp.
@@ -4776,8 +4768,8 @@
when(getServices().iactivityManager.getCurrentUser())
.thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
// Get mock reason string since we throw an IAE with empty string input.
- when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe)).
- thenReturn("Just a test string.");
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Just a test string.");
dpm.wipeData(0);
verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(
@@ -4785,6 +4777,68 @@
}
@Test
+ public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
+
+ // Even if the caller is the managed profile, the current user is the user 0
+ when(getServices().iactivityManager.getCurrentUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ // Get mock reason string since we throw an IAE with empty string input.
+ when(mContext.getResources().getString(R.string.work_profile_deleted_description_dpm_wipe))
+ .thenReturn("Just a test string.");
+ when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE))
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+ when(getServices().userManager.getPrimaryUser())
+ .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+ // Set some device-wide policies:
+ // Security logging
+ when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
+ // System update policy
+ dpms.mOwners.setSystemUpdatePolicy(SystemUpdatePolicy.createAutomaticInstallPolicy());
+ // Make it look as if FRP agent is present.
+ when(dpms.mMockInjector.getPersistentDataBlockManagerInternal().getAllowedUid())
+ .thenReturn(12345 /* some UID in user 0 */);
+ // Make personal apps look suspended
+ dpms.getUserData(UserHandle.USER_SYSTEM).mAppsSuspended = true;
+
+ clearInvocations(getServices().iwindowManager);
+
+ dpm.wipeData(0);
+ verify(getServices().userManagerInternal).removeUserEvenWhenDisallowed(CALLER_USER_HANDLE);
+
+ // Make sure COPE restrictions are lifted:
+ verify(getServices().userManager).setUserRestriction(
+ UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false, UserHandle.SYSTEM);
+ verify(getServices().userManager).setUserRestriction(
+ UserManager.DISALLOW_ADD_USER, false, UserHandle.SYSTEM);
+
+ // Some device-wide policies are getting cleaned-up after the user is removed.
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ sendBroadcastWithUser(dpms, Intent.ACTION_USER_REMOVED, CALLER_USER_HANDLE);
+
+ // Screenlock info should be removed
+ verify(getServices().lockPatternUtils).setDeviceOwnerInfo(null);
+ // Wifi config lockdown should be lifted
+ verify(getServices().settings).settingsGlobalPutInt(
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0);
+ // System update policy should be removed
+ assertThat(dpms.mOwners.getSystemUpdatePolicy()).isNull();
+ // FRP agent should be notified
+ verify(mContext.spiedContext, times(0)).sendBroadcastAsUser(
+ MockUtils.checkIntentAction(
+ DevicePolicyManager.ACTION_RESET_PROTECTION_POLICY_CHANGED),
+ MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
+ // Refresh strong auth timeout and screen capture
+ verify(getServices().lockSettingsInternal).refreshStrongAuthTimeout(UserHandle.USER_SYSTEM);
+ verify(getServices().iwindowManager).refreshScreenCaptureDisabled(UserHandle.USER_SYSTEM);
+ // Unsuspend personal apps
+ verify(getServices().packageManagerInternal)
+ .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+ }
+
+ @Test
public void testWipeDataManagedProfileDisallowed() throws Exception {
final int MANAGED_PROFILE_USER_ID = 15;
final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index e2b6a99..41946fb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -26,6 +26,8 @@
import com.android.internal.R;
+import java.util.HashMap;
+
/**
* Fake class which stubs default system configuration with user-configurable
* settings (useful for testing).
@@ -33,6 +35,8 @@
final class FakeHdmiCecConfig extends HdmiCecConfig {
private static final String TAG = "FakeHdmiCecConfig";
+ private final HashMap<String, String> mSettings = new HashMap<>();
+
public static Context buildContext(Context context) {
Context contextSpy = spy(new ContextWrapper(context));
doReturn(buildResources(context)).when(contextSpy).getResources();
@@ -218,4 +222,15 @@
FakeHdmiCecConfig(@NonNull Context context) {
super(buildContext(context), new StorageAdapter(context));
}
+
+ @Override
+ protected String retrieveValue(@NonNull Setting setting, @NonNull String defaultValue) {
+ return mSettings.getOrDefault(setting.getName(), defaultValue);
+ }
+
+ @Override
+ protected void storeValue(@NonNull Setting setting, @NonNull String value) {
+ mSettings.put(setting.getName(), value);
+ notifySettingChanged(setting);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 68aa96a..b1d77d0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -47,7 +47,6 @@
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
-import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -237,9 +236,6 @@
mHdmiPortInfo[3] =
new HdmiPortInfo(4, HdmiPortInfo.PORT_INPUT, 0x3000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
- mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HdmiControlManager.HDMI_CEC_CONTROL_ENABLED);
mHdmiControlServiceSpy.initService();
mHdmiControlServiceSpy.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
@@ -362,23 +358,6 @@
}
@Test
- public void setAndGetCecVolumeControlEnabled_changesSetting() {
- mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
- HdmiControlManager.VOLUME_CONTROL_DISABLED);
- assertThat(mHdmiControlServiceSpy.readIntSetting(
- Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo(
- HdmiControlManager.VOLUME_CONTROL_DISABLED);
-
- mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
- HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
- HdmiControlManager.VOLUME_CONTROL_ENABLED);
- assertThat(mHdmiControlServiceSpy.readIntSetting(
- Settings.Global.HDMI_CONTROL_VOLUME_CONTROL_ENABLED, -1)).isEqualTo(
- HdmiControlManager.VOLUME_CONTROL_ENABLED);
- }
-
- @Test
public void setAndGetCecVolumeControlEnabledInternal_doesNotChangeSetting() {
mHdmiControlServiceSpy.getHdmiCecConfig().setIntValue(
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 3581206..4a09cf8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -50,12 +50,14 @@
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.GenericDocument;
-import android.app.appsearch.IAppSearchBatchResultCallback;
-import android.app.appsearch.IAppSearchManager;
-import android.app.appsearch.IAppSearchResultCallback;
import android.app.appsearch.PackageIdentifier;
import android.app.appsearch.SearchResultPage;
import android.app.appsearch.SetSchemaResponse;
+import android.app.appsearch.aidl.AppSearchBatchResultParcel;
+import android.app.appsearch.aidl.AppSearchResultParcel;
+import android.app.appsearch.aidl.IAppSearchBatchResultCallback;
+import android.app.appsearch.aidl.IAppSearchManager;
+import android.app.appsearch.aidl.IAppSearchResultCallback;
import android.app.role.OnRoleHoldersChangedListener;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ActivityNotFoundException;
@@ -675,7 +677,9 @@
}
}
final SetSchemaResponse response = new SetSchemaResponse.Builder().build();
- callback.onResult(AppSearchResult.newSuccessfulResult(response.getBundle()));
+ callback.onResult(
+ new AppSearchResultParcel(
+ AppSearchResult.newSuccessfulResult(response.getBundle())));
}
@Override
@@ -711,7 +715,7 @@
}
docMap.put(doc.getId(), doc);
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
@@ -737,7 +741,7 @@
}
}
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
@@ -749,7 +753,8 @@
final Bundle page = new Bundle();
page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
return;
}
final List<GenericDocument> documents = new ArrayList<>(mDocumentMap.get(key).values());
@@ -765,7 +770,8 @@
resultBundles.add(resultBundle);
}
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
}
@Override
@@ -780,7 +786,8 @@
final Bundle page = new Bundle();
page.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, 1);
page.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, new ArrayList<>());
- callback.onResult(AppSearchResult.newSuccessfulResult(page));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(page)));
}
@Override
@@ -835,7 +842,7 @@
}
}
}
- callback.onResult(builder.build());
+ callback.onResult(new AppSearchBatchResultParcel<>(builder.build()));
}
@Override
@@ -844,11 +851,13 @@
throws RemoteException {
final String key = getKey(userId, databaseName);
if (!mDocumentMap.containsKey(key)) {
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
return;
}
mDocumentMap.get(key).clear();
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
}
@Override
@@ -878,7 +887,8 @@
}
private void ignore(IAppSearchResultCallback callback) throws RemoteException {
- callback.onResult(AppSearchResult.newSuccessfulResult(null));
+ callback.onResult(
+ new AppSearchResultParcel<>(AppSearchResult.newSuccessfulResult(null)));
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 4bbea94..ddaf3ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1351,25 +1351,39 @@
* must ensure the visibilities of activities being updated.
*/
@Test
- public void testCompleteFinishing_ensureActivitiesVisible() {
+ public void testCompleteFinishing_ensureActivitiesVisible_withConditions() {
+ testCompleteFinishing_ensureActivitiesVisible(false, PAUSED);
+ testCompleteFinishing_ensureActivitiesVisible(false, STARTED);
+ testCompleteFinishing_ensureActivitiesVisible(true, PAUSED);
+ testCompleteFinishing_ensureActivitiesVisible(true, STARTED);
+ }
+
+ private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
+ ActivityState secondActivityState) {
final ActivityRecord activity = createActivityWithTask();
final Task task = activity.getTask();
final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
firstActivity.mVisibleRequested = false;
firstActivity.nowVisible = false;
- firstActivity.setState(STOPPED, "true");
+ firstActivity.setState(STOPPED, "test");
final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
secondActivity.mVisibleRequested = true;
secondActivity.nowVisible = true;
- secondActivity.setState(PAUSED, "true");
+ secondActivity.setState(secondActivityState, "test");
- final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ ActivityRecord translucentActivity;
+ if (diffTask) {
+ translucentActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ } else {
+ translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
+ }
translucentActivity.mVisibleRequested = true;
translucentActivity.nowVisible = true;
- translucentActivity.setState(RESUMED, "true");
+ translucentActivity.setState(RESUMED, "test");
- doReturn(false).when(translucentActivity).occludesParent();
+ doReturn(true).when(firstActivity).occludesParent(true);
+ doReturn(true).when(secondActivity).occludesParent(true);
// Finish the second activity
secondActivity.finishing = true;
@@ -1385,6 +1399,10 @@
verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
0 /* configChanges */ , false /* preserveWindows */,
true /* notifyClients */);
+
+ // Remove the translucent activity and clear invocations for next test
+ translucentActivity.getTask().removeImmediately("test");
+ clearInvocations(mDefaultDisplay);
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 748622b..e280a36 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -38,6 +38,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.Task.ActivityState.DESTROYED;
import static com.android.server.wm.Task.ActivityState.DESTROYING;
import static com.android.server.wm.Task.ActivityState.FINISHING;
import static com.android.server.wm.Task.ActivityState.PAUSING;
@@ -1204,6 +1205,9 @@
// See {@link ActivityStack#destroyActivityLocked}.
activity.app = null;
overlayActivity.app = null;
+ // Simulate the process is dead
+ activity.mVisibleRequested = false;
+ activity.setState(DESTROYED, "Test");
assertEquals(2, task.getChildCount());
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
index 0b47547..e9b7d95 100644
--- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
+++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony;
+package com.android.internal.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 0d6cd5a..98db291 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -43,7 +43,6 @@
import android.os.UserManager;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
-import android.telephony.PackageChangeReceiver;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.SparseArray;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 84ea42f..33a9a96 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3473,6 +3473,21 @@
"additional_nr_advanced_bands_int_array";
/**
+ * This configuration allows the framework to control the NR advanced capable by protocol
+ * configuration options(PCO).
+ *
+ * If this config is 0, then the nr advanced capable is enabled.
+ * If this config is not 0 and PCO container with this config's address is 1, then the nr
+ * advanced capable is enabled.
+ * If this config is not 0 and PCO container with this config's address is 0, then the nr
+ * advanced capable is disabled.
+ *
+ * @hide
+ */
+ public static final String KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT =
+ "nr_advanced_capable_pco_id_int";
+
+ /**
* Controls time in milliseconds until DcTracker reevaluates 5G connection state.
* @hide
*/
@@ -5436,6 +5451,7 @@
/* Default value is 1 hour. */
sDefaults.putLong(KEY_5G_WATCHDOG_TIME_MS_LONG, 3600000);
sDefaults.putIntArray(KEY_ADDITIONAL_NR_ADVANCED_BANDS_INT_ARRAY, new int[0]);
+ sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 9bd639b..b58aa11 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -32,7 +32,7 @@
java_sdk_library {
name: "android.test.base",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-base-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -66,7 +66,7 @@
name: "android.test.base_static",
installable: false,
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-base-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -114,6 +114,12 @@
],
}
+filegroup {
+ name: "android-test-base-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
+
// Make the current.txt available for use by the cts/tests/signature tests.
// ========================================================================
filegroup {
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index b83bce6..107292c 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -29,7 +29,7 @@
name: "android.test.mock",
srcs: [
- "src/**/*.java",
+ ":android-test-mock-sources",
// Note: Below are NOT APIs of this library. We only take APIs under
// the android.test.mock package. They however provide private APIs that
// android.test.mock APIs references to.
@@ -61,3 +61,9 @@
"api/current.txt",
],
}
+
+filegroup {
+ name: "android-test-mock-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index fe007e39..c380ae3 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -29,7 +29,7 @@
java_sdk_library {
name: "android.test.runner",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-runner-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -76,7 +76,7 @@
java_library_static {
name: "repackaged.android.test.runner",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-runner-sources"],
exclude_srcs: [
"src/android/test/ActivityUnitTestCase.java",
"src/android/test/ApplicationTestCase.java",
@@ -108,3 +108,9 @@
"api/current.txt",
],
}
+
+filegroup {
+ name: "android-test-runner-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index f2c3b86..812e208 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1121,8 +1121,8 @@
// Skip all "uses-sdk" tags besides the very last tag. The android runtime only uses
// the attribute values from the last defined tag.
- for (size_t i = 0; i < usesSdkTagPositions.size() - 1; i++) {
- tagsToSkip.emplace_back(usesSdkTagPositions[i]);
+ for (size_t i = 1; i < usesSdkTagPositions.size(); i++) {
+ tagsToSkip.emplace_back(usesSdkTagPositions[i - 1]);
}
// Reset the position before parsing.
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 77c0872..ef3a62f 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -278,17 +278,19 @@
printer->Println(StringPrintf(" entryCount=%zd", type.entries.size()));
printer->Indent();
- for (const ResourceEntry* entry : type.entries) {
+ for (const ResourceTableEntryView& entry : type.entries) {
printer->Print("resource ");
- printer->Print(entry->id.value_or_default(0).to_string());
+ printer->Print(ResourceId(package.id.value_or_default(0), type.id.value_or_default(0),
+ entry.id.value_or_default(0))
+ .to_string());
printer->Print(" ");
// Write the name without the package (this is obvious and too verbose).
printer->Print(to_string(type.type));
printer->Print("/");
- printer->Print(entry->name);
+ printer->Print(entry.name);
- switch (entry->visibility.level) {
+ switch (entry.visibility.level) {
case Visibility::Level::kPublic:
printer->Print(" PUBLIC");
break;
@@ -300,19 +302,24 @@
break;
}
- if (entry->visibility.staged_api) {
+ if (entry.visibility.staged_api) {
printer->Print(" STAGED");
}
- if (entry->overlayable_item) {
+ if (entry.overlayable_item) {
printer->Print(" OVERLAYABLE");
}
+ if (entry.staged_id) {
+ printer->Print(" STAGED_ID=");
+ printer->Print(entry.staged_id.value().id.to_string());
+ }
+
printer->Println();
if (options.show_values) {
printer->Indent();
- for (const auto& value : entry->values) {
+ for (const auto& value : entry.values) {
printer->Print("(");
printer->Print(value->config.to_string());
printer->Print(") ");
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1efabbb..f1e2da9 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -45,6 +45,7 @@
namespace {
constexpr const char* kPublicGroupTag = "public-group";
constexpr const char* kStagingPublicGroupTag = "staging-public-group";
+constexpr const char* kStagingPublicGroupFinalTag = "staging-public-group-final";
} // namespace
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -109,6 +110,7 @@
bool staged_api = false;
bool allow_new = false;
Maybe<OverlayableItem> overlayable_item;
+ Maybe<StagedId> staged_alias;
std::string comment;
std::unique_ptr<Value> value;
@@ -155,6 +157,10 @@
res_builder.SetValue(std::move(res->value), res->config, res->product);
}
+ if (res->staged_alias) {
+ res_builder.SetStagedId(res->staged_alias.value());
+ }
+
bool error = false;
if (!res->name.entry.empty()) {
if (!table->AddResource(res_builder.Build(), diag)) {
@@ -532,6 +538,7 @@
{"public", std::mem_fn(&ResourceParser::ParsePublic)},
{"public-group", std::mem_fn(&ResourceParser::ParsePublicGroup)},
{"staging-public-group", std::mem_fn(&ResourceParser::ParseStagingPublicGroup)},
+ {"staging-public-group-final", std::mem_fn(&ResourceParser::ParseStagingPublicGroupFinal)},
{"string-array", std::mem_fn(&ResourceParser::ParseStringArray)},
{"style", std::bind(&ResourceParser::ParseStyle, std::placeholders::_1, ResourceType::kStyle,
std::placeholders::_2, std::placeholders::_3)},
@@ -671,7 +678,7 @@
if (bag_iter != elToBagMap.end()) {
// Ensure we have a name (unless this is a <public-group> or <overlayable>).
if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag &&
- resource_type != "overlayable") {
+ resource_type != kStagingPublicGroupFinalTag && resource_type != "overlayable") {
if (!maybe_name) {
diag_->Error(DiagMessage(out_resource->source)
<< "<" << parser->element_name() << "> missing 'name' attribute");
@@ -1034,7 +1041,6 @@
ParsedResource& entry_res = out_resource->child_resources.emplace_back(ParsedResource{
.name = ResourceName{{}, *parsed_type, maybe_name.value().to_string()},
.source = item_source,
- .id = next_id,
.comment = std::move(comment),
});
@@ -1060,6 +1066,14 @@
});
}
+bool ResourceParser::ParseStagingPublicGroupFinal(xml::XmlPullParser* parser,
+ ParsedResource* out_resource) {
+ return ParseGroupImpl(parser, out_resource, kStagingPublicGroupFinalTag, diag_,
+ [](ParsedResource& parsed_entry, ResourceId id) {
+ parsed_entry.staged_alias = StagedId{id, parsed_entry.source};
+ });
+}
+
bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
if (options_.visibility) {
diag_->Error(DiagMessage(out_resource->source)
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 5c92def..2614997 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -115,6 +115,7 @@
bool ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseStagingPublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource);
+ bool ParseStagingPublicGroupFinal(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 45ea654..8ab1493 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -83,6 +83,20 @@
return action(found, iter);
}
+struct ConfigKey {
+ const ConfigDescription* config;
+ const StringPiece& product;
+};
+
+template <typename T>
+bool lt_config_key_ref(const T& lhs, const ConfigKey& rhs) {
+ int cmp = lhs->config.compare(*rhs.config);
+ if (cmp == 0) {
+ cmp = StringPiece(lhs->product).compare(rhs.product);
+ }
+ return cmp < 0;
+}
+
} // namespace
ResourceTable::ResourceTable(ResourceTable::Validation validation) : validation_(validation) {
@@ -134,23 +148,10 @@
});
}
-struct ConfigKey {
- const ConfigDescription* config;
- const StringPiece& product;
-};
-
-bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
- int cmp = lhs->config.compare(*rhs.config);
- if (cmp == 0) {
- cmp = StringPiece(lhs->product).compare(rhs.product);
- }
- return cmp < 0;
-}
-
ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
android::StringPiece product) {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -163,7 +164,7 @@
const ResourceConfigValue* ResourceEntry::FindValue(const android::ConfigDescription& config,
android::StringPiece product) const {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -176,7 +177,7 @@
ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
const StringPiece& product) {
auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
- lt_config_key_ref);
+ lt_config_key_ref<std::unique_ptr<ResourceConfigValue>>);
if (iter != values.end()) {
ResourceConfigValue* value = iter->get();
if (value->config == config && StringPiece(value->product) == product) {
@@ -296,6 +297,7 @@
return CollisionResult::kConflict;
}
+namespace {
template <typename T, typename Comparer>
struct SortedVectorInserter : public Comparer {
std::pair<bool, typename std::vector<T>::iterator> LowerBound(std::vector<T>& el,
@@ -313,7 +315,7 @@
if (found) {
return &*it;
}
- return &*el.insert(it, std::move(value));
+ return &*el.insert(it, std::forward<T>(value));
}
};
@@ -331,35 +333,77 @@
};
struct EntryViewComparer {
- bool operator()(const ResourceEntry* lhs, const ResourceEntry* rhs) {
- return less_than_struct_with_name_and_id<ResourceEntry, ResourceId>(
- *lhs, std::make_pair(rhs->name, rhs->id));
+ bool operator()(const ResourceTableEntryView& lhs, const ResourceTableEntryView& rhs) {
+ return less_than_struct_with_name_and_id<ResourceTableEntryView, uint16_t>(
+ lhs, std::make_pair(rhs.name, rhs.id));
}
};
-ResourceTableView ResourceTable::GetPartitionedView() const {
- ResourceTableView view;
+void InsertEntryIntoTableView(ResourceTableView& table, const ResourceTablePackage* package,
+ const ResourceTableType* type, const std::string& entry_name,
+ const Maybe<ResourceId>& id, const Visibility& visibility,
+ const Maybe<AllowNew>& allow_new,
+ const Maybe<OverlayableItem>& overlayable_item,
+ const Maybe<StagedId>& staged_id,
+ const std::vector<std::unique_ptr<ResourceConfigValue>>& values) {
SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
- SortedVectorInserter<const ResourceEntry*, EntryViewComparer> entry_inserter;
+ SortedVectorInserter<ResourceTableEntryView, EntryViewComparer> entry_inserter;
+ ResourceTablePackageView new_package{package->name,
+ id ? id.value().package_id() : Maybe<uint8_t>{}};
+ auto view_package = package_inserter.Insert(table.packages, std::move(new_package));
+
+ ResourceTableTypeView new_type{type->type, id ? id.value().type_id() : Maybe<uint8_t>{}};
+ auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
+
+ if (visibility.level == Visibility::Level::kPublic) {
+ // Only mark the type visibility level as public, it doesn't care about being private.
+ view_type->visibility_level = Visibility::Level::kPublic;
+ }
+
+ ResourceTableEntryView new_entry{.name = entry_name,
+ .id = id ? id.value().entry_id() : Maybe<uint16_t>{},
+ .visibility = visibility,
+ .allow_new = allow_new,
+ .overlayable_item = overlayable_item,
+ .staged_id = staged_id};
+ for (auto& value : values) {
+ new_entry.values.emplace_back(value.get());
+ }
+
+ entry_inserter.Insert(view_type->entries, std::move(new_entry));
+}
+} // namespace
+
+const ResourceConfigValue* ResourceTableEntryView::FindValue(const ConfigDescription& config,
+ android::StringPiece product) const {
+ auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
+ lt_config_key_ref<const ResourceConfigValue*>);
+ if (iter != values.end()) {
+ const ResourceConfigValue* value = *iter;
+ if (value->config == config && StringPiece(value->product) == product) {
+ return value;
+ }
+ }
+ return nullptr;
+}
+
+ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptions& options) const {
+ ResourceTableView view;
for (const auto& package : packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
- ResourceTablePackageView new_package{
- package->name, entry->id ? entry->id.value().package_id() : Maybe<uint8_t>{}};
- auto view_package = package_inserter.Insert(view.packages, std::move(new_package));
+ InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, entry->id,
+ entry->visibility, entry->allow_new, entry->overlayable_item,
+ entry->staged_id, entry->values);
- ResourceTableTypeView new_type{type->type,
- entry->id ? entry->id.value().type_id() : Maybe<uint8_t>{}};
- auto view_type = type_inserter.Insert(view_package->types, std::move(new_type));
-
- if (entry->visibility.level == Visibility::Level::kPublic) {
- // Only mark the type visibility level as public, it doesn't care about being private.
- view_type->visibility_level = Visibility::Level::kPublic;
+ if (options.create_alias_entries && entry->staged_id) {
+ auto alias_id = entry->staged_id.value().id;
+ InsertEntryIntoTableView(view, package.get(), type.get(), entry->name, alias_id,
+ entry->visibility, entry->allow_new, entry->overlayable_item, {},
+ entry->values);
}
-
- entry_inserter.Insert(view_type->entries, entry.get());
}
}
}
@@ -368,6 +412,8 @@
// for the same resource type within the same package. For this reason, if there are types with
// multiple type ids, each type needs to exist in its own package in order to be queried by name.
std::vector<ResourceTablePackageView> new_packages;
+ SortedVectorInserter<ResourceTablePackageView, PackageViewComparer> package_inserter;
+ SortedVectorInserter<ResourceTableTypeView, TypeViewComparer> type_inserter;
for (auto& package : view.packages) {
// If a new package was already created for a different type within this package, then
// we can reuse those packages for other types that need to be extracted from this package.
@@ -498,6 +544,10 @@
entry->allow_new = res.allow_new.value();
}
+ if (res.staged_id.has_value()) {
+ entry->staged_id = res.staged_id.value();
+ }
+
if (res.value != nullptr) {
auto config_value = entry->FindOrCreateValue(res.config, res.product);
if (!config_value->value) {
@@ -575,6 +625,28 @@
return {};
}
+bool ResourceTable::RemoveResource(const ResourceNameRef& name, ResourceId id) const {
+ ResourceTablePackage* package = FindPackage(name.package);
+ if (package == nullptr) {
+ return {};
+ }
+
+ ResourceTableType* type = package->FindType(name.type);
+ if (type == nullptr) {
+ return {};
+ }
+
+ auto entry_it = std::equal_range(type->entries.begin(), type->entries.end(), name.entry,
+ NameEqualRange<ResourceEntry>{});
+ for (auto it = entry_it.first; it != entry_it.second; ++it) {
+ if ((*it)->id == id) {
+ type->entries.erase(it);
+ return true;
+ }
+ }
+ return false;
+}
+
std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
CloningValueTransformer cloner(&new_table->string_pool);
@@ -640,6 +712,11 @@
return *this;
}
+NewResourceBuilder& NewResourceBuilder::SetStagedId(StagedId staged_alias) {
+ res_.staged_id = std::move(staged_alias);
+ return *this;
+}
+
NewResourceBuilder& NewResourceBuilder::SetAllowMangled(bool allow_mangled) {
res_.allow_mangled = allow_mangled;
return *this;
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 080ecc2..bae1d82 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -64,6 +64,12 @@
std::string comment;
};
+// Represents the staged resource id of a finalized resource.
+struct StagedId {
+ ResourceId id;
+ Source source;
+};
+
struct Overlayable {
Overlayable() = default;
Overlayable(const android::StringPiece& name, const android::StringPiece& actor)
@@ -124,6 +130,9 @@
// The declarations of this resource as overlayable for RROs
Maybe<OverlayableItem> overlayable_item;
+ // The staged resource id for a finalized resource.
+ Maybe<StagedId> staged_id;
+
// The resource's values for each configuration.
std::vector<std::unique_ptr<ResourceConfigValue>> values;
@@ -194,14 +203,27 @@
DISALLOW_COPY_AND_ASSIGN(ResourceTablePackage);
};
+struct ResourceTableEntryView {
+ std::string name;
+ Maybe<uint16_t> id;
+ Visibility visibility;
+ Maybe<AllowNew> allow_new;
+ Maybe<OverlayableItem> overlayable_item;
+ Maybe<StagedId> staged_id;
+ std::vector<const ResourceConfigValue*> values;
+
+ const ResourceConfigValue* FindValue(const android::ConfigDescription& config,
+ android::StringPiece product = {}) const;
+};
+
struct ResourceTableTypeView {
ResourceType type;
Maybe<uint8_t> id;
Visibility::Level visibility_level = Visibility::Level::kUndefined;
// Entries sorted in ascending entry id order. If ids have not been assigned, the entries are
- // // sorted lexicographically.
- std::vector<const ResourceEntry*> entries;
+ // sorted lexicographically.
+ std::vector<ResourceTableEntryView> entries;
};
struct ResourceTablePackageView {
@@ -212,6 +234,10 @@
std::vector<ResourceTableTypeView> types;
};
+struct ResourceTableViewOptions {
+ bool create_alias_entries = false;
+};
+
struct ResourceTableView {
// Packages sorted in ascending package id order. If ids have not been assigned, the packages are
// sorted lexicographically.
@@ -237,6 +263,7 @@
std::optional<Visibility> visibility;
std::optional<OverlayableItem> overlayable;
std::optional<AllowNew> allow_new;
+ std::optional<StagedId> staged_id;
bool allow_mangled = false;
};
@@ -249,6 +276,7 @@
NewResourceBuilder& SetVisibility(Visibility id);
NewResourceBuilder& SetOverlayable(OverlayableItem overlayable);
NewResourceBuilder& SetAllowNew(AllowNew allow_new);
+ NewResourceBuilder& SetStagedId(StagedId id);
NewResourceBuilder& SetAllowMangled(bool allow_mangled);
NewResource Build();
@@ -273,7 +301,7 @@
// Retrieves a sorted a view of the packages, types, and entries sorted in ascending resource id
// order.
- ResourceTableView GetPartitionedView() const;
+ ResourceTableView GetPartitionedView(const ResourceTableViewOptions& options = {}) const;
struct SearchResult {
ResourceTablePackage* package;
@@ -283,6 +311,7 @@
Maybe<SearchResult> FindResource(const ResourceNameRef& name) const;
Maybe<SearchResult> FindResource(const ResourceNameRef& name, ResourceId id) const;
+ bool RemoveResource(const ResourceNameRef& name, ResourceId id) const;
// Returns the package struct with the given name, or nullptr if such a package does not
// exist. The empty string is a valid package and typically is used to represent the
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b45c040..95b7949 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -190,6 +190,12 @@
uint32 overlayable_idx = 4;
}
+// The staged resource ID definition of a finalized resource.
+message StagedId {
+ Source source = 1;
+ uint32 staged_id = 2;
+}
+
// An entry ID in the range [0x0000, 0xffff].
message EntryId {
uint32 id = 1;
@@ -222,6 +228,9 @@
// The set of values defined for this entry, each corresponding to a different
// configuration/variant.
repeated ConfigValue config_value = 6;
+
+ // The staged resource ID of this finalized resource.
+ StagedId staged_id = 7;
}
// A Configuration/Value pair.
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index df31087..3950f33 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -97,15 +97,15 @@
static bool EmitResourceConfigValueDiff(
IAaptContext* context, LoadedApk* apk_a, const ResourceTablePackageView& pkg_a,
- const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
+ const ResourceTableTypeView& type_a, const ResourceTableEntryView& entry_a,
const ResourceConfigValue* config_value_a, LoadedApk* apk_b,
const ResourceTablePackageView& pkg_b, const ResourceTableTypeView& type_b,
- const ResourceEntry* entry_b, const ResourceConfigValue* config_value_b) {
+ const ResourceTableEntryView& entry_b, const ResourceConfigValue* config_value_b) {
Value* value_a = config_value_a->value.get();
Value* value_b = config_value_b->value.get();
if (!value_a->Equals(value_b)) {
std::stringstream str_stream;
- str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << "value " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " config=" << config_value_a->config << " does not match:\n";
value_a->Print(&str_stream);
str_stream << "\n vs \n";
@@ -118,32 +118,32 @@
static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
const ResourceTablePackageView& pkg_a,
- const ResourceTableTypeView& type_a, const ResourceEntry* entry_a,
- LoadedApk* apk_b, const ResourceTablePackageView& pkg_b,
+ const ResourceTableTypeView& type_a,
+ const ResourceTableEntryView& entry_a, LoadedApk* apk_b,
+ const ResourceTablePackageView& pkg_b,
const ResourceTableTypeView& type_b,
- const ResourceEntry* entry_b) {
+ const ResourceTableEntryView& entry_b) {
bool diff = false;
- for (const std::unique_ptr<ResourceConfigValue>& config_value_a : entry_a->values) {
- auto config_value_b = entry_b->FindValue(config_value_a->config);
+ for (const ResourceConfigValue* config_value_a : entry_a.values) {
+ auto config_value_b = entry_b.FindValue(config_value_a->config);
if (!config_value_b) {
std::stringstream str_stream;
- str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " config=" << config_value_a->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
- diff |=
- EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a.get(),
- apk_b, pkg_b, type_b, entry_b, config_value_b);
+ diff |= EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a,
+ apk_b, pkg_b, type_b, entry_b, config_value_b);
}
}
// Check for any newly added config values.
- for (const std::unique_ptr<ResourceConfigValue>& config_value_b : entry_b->values) {
- auto config_value_a = entry_a->FindValue(config_value_b->config);
+ for (const ResourceConfigValue* config_value_b : entry_b.values) {
+ auto config_value_a = entry_a.FindValue(config_value_b->config);
if (!config_value_a) {
std::stringstream str_stream;
- str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b->name
+ str_stream << "new config " << pkg_b.name << ":" << type_b.type << "/" << entry_b.name
<< " config=" << config_value_b->config;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
@@ -164,36 +164,35 @@
if (entry_b_iter == type_b.entries.end()) {
// Type A contains a type that type B does not have.
std::stringstream str_stream;
- str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << (*entry_a_iter)->name;
+ str_stream << "missing " << pkg_a.name << ":" << type_a.type << "/" << entry_a_iter->name;
EmitDiffLine(apk_a->GetSource(), str_stream.str());
diff = true;
} else if (entry_a_iter == type_a.entries.end()) {
// Type B contains a type that type A does not have.
std::stringstream str_stream;
- str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/"
- << (*entry_b_iter)->name;
+ str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/" << entry_b_iter->name;
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
} else {
const auto& entry_a = *entry_a_iter;
const auto& entry_b = *entry_b_iter;
- if (IsSymbolVisibilityDifferent(entry_a->visibility, entry_b->visibility)) {
+ if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
std::stringstream str_stream;
- str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " has different visibility (";
- if (entry_b->visibility.staged_api) {
+ if (entry_b.visibility.staged_api) {
str_stream << "STAGED ";
}
- if (entry_b->visibility.level == Visibility::Level::kPublic) {
+ if (entry_b.visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
}
str_stream << " vs ";
- if (entry_a->visibility.staged_api) {
+ if (entry_a.visibility.staged_api) {
str_stream << "STAGED ";
}
- if (entry_a->visibility.level == Visibility::Level::kPublic) {
+ if (entry_a.visibility.level == Visibility::Level::kPublic) {
str_stream << "PUBLIC";
} else {
str_stream << "PRIVATE";
@@ -201,19 +200,19 @@
str_stream << ")";
EmitDiffLine(apk_b->GetSource(), str_stream.str());
diff = true;
- } else if (IsIdDiff(entry_a->visibility.level, entry_a->id, entry_b->visibility.level,
- entry_b->id)) {
+ } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
+ entry_b.id)) {
std::stringstream str_stream;
- str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a->name
+ str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
<< " has different public ID (";
- if (entry_b->id) {
- str_stream << "0x" << std::hex << entry_b->id.value();
+ if (entry_b.id) {
+ str_stream << "0x" << std::hex << entry_b.id.value();
} else {
str_stream << "none";
}
str_stream << " vs ";
- if (entry_a->id) {
- str_stream << "0x " << std::hex << entry_a->id.value();
+ if (entry_a.id) {
+ str_stream << "0x " << std::hex << entry_a.id.value();
} else {
str_stream << "none";
}
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 3118eb8..430c184 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -402,8 +402,39 @@
EXPECT_THAT(client_r_contents, HasSubstr(" com.example.lib.R.attr.foo, 0x7f010000"));
}
-TEST_F(LinkTest, StagedAndroidApi) {
- StdErrDiagnostics diag;
+struct SourceXML {
+ std::string res_file_path;
+ std::string file_contents;
+};
+
+static void BuildApk(const std::vector<SourceXML>& source_files, const std::string& apk_path,
+ LinkCommandBuilder&& link_args, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
+ TemporaryDir res_dir;
+ TemporaryDir compiled_res_dir;
+ for (auto& source_file : source_files) {
+ ASSERT_TRUE(fixture->CompileFile(res_dir.path + source_file.res_file_path,
+ source_file.file_contents, compiled_res_dir.path, diag));
+ }
+ ASSERT_TRUE(fixture->Link(
+ link_args.AddCompiledResDir(compiled_res_dir.path, diag).Build(apk_path), diag));
+}
+
+static void BuildSDK(const std::vector<SourceXML>& source_files, const std::string& apk_path,
+ const std::string& java_root_path, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
+ auto android_manifest = ManifestBuilder(fixture).SetPackageName("android").Build();
+
+ auto android_link_args = LinkCommandBuilder(fixture)
+ .SetManifestFile(android_manifest)
+ .AddParameter("--private-symbols", "com.android.internal")
+ .AddParameter("--java", java_root_path);
+
+ BuildApk(source_files, apk_path, std::move(android_link_args), fixture, diag);
+}
+
+static void BuildNonFinalizedSDK(const std::string& apk_path, const std::string& java_path,
+ CommandTestFixture* fixture, IDiagnostics* diag) {
const std::string android_values =
R"(<resources>
<public type="attr" name="finalized_res" id="0x01010001"/>
@@ -413,6 +444,10 @@
<public name="staged_s_res" />
</staging-public-group>
+ <staging-public-group type="string" first-id="0x01fd0080">
+ <public name="staged_s_string" />
+ </staging-public-group>
+
<!-- SV2 staged attributes (support staged resources in a separate type id) -->
<staging-public-group type="attr" first-id="0x01ff0049">
<public name="staged_s2_res" />
@@ -423,46 +458,90 @@
<public name="staged_t_res" />
</staging-public-group>
- <staging-public-group type="string" first-id="0x01fd0072">
- <public name="staged_t_string" />
+ <attr name="finalized_res" />
+ <attr name="staged_s_res" />
+ <attr name="staged_s2_res" />
+ <attr name="staged_t_res" />
+ <string name="staged_s_string">Hello</string>
+ </resources>)";
+
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
+ BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
+}
+
+static void BuildFinalizedSDK(const std::string& apk_path, const std::string& java_path,
+ CommandTestFixture* fixture, IDiagnostics* diag) {
+ const std::string android_values =
+ R"(<resources>
+ <public type="attr" name="finalized_res" id="0x01010001"/>
+ <public type="attr" name="staged_s_res" id="0x01010002"/>
+ <public type="attr" name="staged_s2_res" id="0x01010003"/>
+ <public type="string" name="staged_s_string" id="0x01020000"/>
+
+ <!-- S staged attributes (support staged resources in the same type id) -->
+ <staging-public-group-final type="attr" first-id="0x01010050">
+ <public name="staged_s_res" />
+ </staging-public-group-final>
+
+ <staging-public-group-final type="string" first-id="0x01fd0080">
+ <public name="staged_s_string" />
+ </staging-public-group-final>
+
+ <!-- SV2 staged attributes (support staged resources in a separate type id) -->
+ <staging-public-group-final type="attr" first-id="0x01ff0049">
+ <public name="staged_s2_res" />
+ </staging-public-group-final>
+
+ <!-- T staged attributes (support staged resources in multiple separate type ids) -->
+ <staging-public-group type="attr" first-id="0x01fe0063">
+ <public name="staged_t_res" />
</staging-public-group>
<attr name="finalized_res" />
<attr name="staged_s_res" />
<attr name="staged_s2_res" />
<attr name="staged_t_res" />
- <string name="staged_t_string">Hello</string>
+ <string name="staged_s_string">Hello</string>
</resources>)";
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = android_values};
+ BuildSDK({source_xml}, apk_path, java_path, fixture, diag);
+}
+
+static void BuildAppAgainstSDK(const std::string& apk_path, const std::string& java_path,
+ const std::string& sdk_path, CommandTestFixture* fixture,
+ IDiagnostics* diag) {
const std::string app_values =
R"(<resources xmlns:android="http://schemas.android.com/apk/res/android">
<attr name="bar" />
+ <style name="MyStyle">
+ <item name="android:staged_s_res">@android:string/staged_s_string</item>
+ </style>
<declare-styleable name="ClientStyleable">
<attr name="android:finalized_res" />
<attr name="android:staged_s_res" />
<attr name="bar" />
</declare-styleable>
+ <public name="MyStyle" type="style" id="0x7f020000" />
</resources>)";
- const std::string android_res = GetTestPath("android-res");
- ASSERT_TRUE(
- CompileFile(GetTestPath("res/values/values.xml"), android_values, android_res, &diag));
+ SourceXML source_xml{.res_file_path = "/res/values/values.xml", .file_contents = app_values};
+ auto app_manifest = ManifestBuilder(fixture).SetPackageName("com.example.app").Build();
+
+ auto app_link_args = LinkCommandBuilder(fixture)
+ .SetManifestFile(app_manifest)
+ .AddParameter("--java", java_path)
+ .AddParameter("-I", sdk_path);
+
+ BuildApk({source_xml}, apk_path, std::move(app_link_args), fixture, diag);
+}
+
+TEST_F(LinkTest, StagedAndroidApi) {
+ StdErrDiagnostics diag;
const std::string android_apk = GetTestPath("android.apk");
- const std::string android_java = GetTestPath("android_java");
- // clang-format off
- auto android_manifest = ManifestBuilder(this)
- .SetPackageName("android")
- .Build();
-
- auto android_link_args = LinkCommandBuilder(this)
- .SetManifestFile(android_manifest)
- .AddParameter("--private-symbols", "com.android.internal")
- .AddParameter("--java", android_java)
- .AddCompiledResDir(android_res, &diag)
- .Build(android_apk);
- // clang-format on
- ASSERT_TRUE(Link(android_link_args, &diag));
+ const std::string android_java = GetTestPath("android-java");
+ BuildNonFinalizedSDK(android_apk, android_java, this, &diag);
const std::string android_r_java = android_java + "/android/R.java";
std::string android_r_contents;
@@ -473,33 +552,17 @@
HasSubstr("public static final int staged_s_res; static { staged_s_res=0x01010050; }"));
EXPECT_THAT(
android_r_contents,
+ HasSubstr("public static final int staged_s_string; static { staged_s_string=0x01fd0080; }"));
+ EXPECT_THAT(
+ android_r_contents,
HasSubstr("public static final int staged_s2_res; static { staged_s2_res=0x01ff0049; }"));
EXPECT_THAT(
android_r_contents,
HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
- EXPECT_THAT(
- android_r_contents,
- HasSubstr("public static final int staged_t_string; static { staged_t_string=0x01fd0072; }"));
-
- // Build an app that uses the framework attribute in a declare-styleable
- const std::string client_res = GetTestPath("app-res");
- ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), app_values, client_res, &diag));
const std::string app_apk = GetTestPath("app.apk");
- const std::string app_java = GetTestPath("app_java");
- // clang-format off
- auto app_manifest = ManifestBuilder(this)
- .SetPackageName("com.example.app")
- .Build();
-
- auto app_link_args = LinkCommandBuilder(this)
- .SetManifestFile(app_manifest)
- .AddParameter("--java", app_java)
- .AddParameter("-I", android_apk)
- .AddCompiledResDir(client_res, &diag)
- .Build(app_apk);
- // clang-format on
- ASSERT_TRUE(Link(app_link_args, &diag));
+ const std::string app_java = GetTestPath("app-java");
+ BuildAppAgainstSDK(app_apk, app_java, android_apk, this, &diag);
const std::string client_r_java = app_java + "/com/example/app/R.java";
std::string client_r_contents;
@@ -520,6 +583,10 @@
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01010050));
+ result = am.GetResourceId("android:string/staged_s_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01fd0080));
+
result = am.GetResourceId("android:attr/staged_s2_res");
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01ff0049));
@@ -527,10 +594,88 @@
result = am.GetResourceId("android:attr/staged_t_res");
ASSERT_TRUE(result.has_value());
EXPECT_THAT(*result, Eq(0x01fe0063));
+}
- result = am.GetResourceId("android:string/staged_t_string");
+TEST_F(LinkTest, FinalizedAndroidApi) {
+ StdErrDiagnostics diag;
+ const std::string android_apk = GetTestPath("android.apk");
+ const std::string android_java = GetTestPath("android-java");
+ BuildFinalizedSDK(android_apk, android_java, this, &diag);
+
+ const std::string android_r_java = android_java + "/android/R.java";
+ std::string android_r_contents;
+ ASSERT_TRUE(android::base::ReadFileToString(android_r_java, &android_r_contents));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int finalized_res=0x01010001;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_res=0x01010002;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s_string=0x01020000;"));
+ EXPECT_THAT(android_r_contents, HasSubstr("public static final int staged_s2_res=0x01010003;"));
+ EXPECT_THAT(
+ android_r_contents,
+ HasSubstr("public static final int staged_t_res; static { staged_t_res=0x01fe0063; }"));
+ ;
+
+ // Build an application against the non-finalized SDK and then load it into an AssetManager with
+ // the finalized SDK.
+ const std::string non_finalized_android_apk = GetTestPath("non-finalized-android.apk");
+ const std::string non_finalized_android_java = GetTestPath("non-finalized-android-java");
+ BuildNonFinalizedSDK(non_finalized_android_apk, non_finalized_android_java, this, &diag);
+
+ const std::string app_apk = GetTestPath("app.apk");
+ const std::string app_java = GetTestPath("app-java");
+ BuildAppAgainstSDK(app_apk, app_java, non_finalized_android_apk, this, &diag);
+
+ android::AssetManager2 am;
+ auto android_asset = android::ApkAssets::Load(android_apk);
+ auto app_against_non_final = android::ApkAssets::Load(app_apk);
+ ASSERT_THAT(android_asset, NotNull());
+ ASSERT_THAT(app_against_non_final, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_non_final.get()}));
+
+ auto result = am.GetResourceId("android:attr/finalized_res");
ASSERT_TRUE(result.has_value());
- EXPECT_THAT(*result, Eq(0x01fd0072));
+ EXPECT_THAT(*result, Eq(0x01010001));
+
+ result = am.GetResourceId("android:attr/staged_s_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010002));
+
+ result = am.GetResourceId("android:string/staged_s_string");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01020000));
+
+ result = am.GetResourceId("android:attr/staged_s2_res");
+ ASSERT_TRUE(result.has_value());
+ EXPECT_THAT(*result, Eq(0x01010003));
+
+ {
+ auto style = am.GetBag(0x7f020000);
+ ASSERT_TRUE(style.has_value());
+
+ auto& entry = (*style)->entries[0];
+ EXPECT_THAT(entry.key, Eq(0x01010002));
+ EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(entry.value.data, Eq(0x01020000));
+ }
+
+ // Re-compile the application against the finalized SDK and then load it into an AssetManager with
+ // the finalized SDK.
+ const std::string app_apk_respin = GetTestPath("app-respin.apk");
+ const std::string app_java_respin = GetTestPath("app-respin-java");
+ BuildAppAgainstSDK(app_apk_respin, app_java_respin, android_apk, this, &diag);
+
+ auto app_against_final = android::ApkAssets::Load(app_apk_respin);
+ ASSERT_THAT(app_against_final, NotNull());
+ ASSERT_TRUE(am.SetApkAssets({android_asset.get(), app_against_final.get()}));
+
+ {
+ auto style = am.GetBag(0x7f020000);
+ ASSERT_TRUE(style.has_value());
+
+ auto& entry = (*style)->entries[0];
+ EXPECT_THAT(entry.key, Eq(0x01010002));
+ EXPECT_THAT(entry.value.dataType, Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(entry.value.data, Eq(0x01020000));
+ }
}
TEST_F(LinkTest, MacroSubstitution) {
diff --git a/tools/aapt2/compile/IdAssigner.cpp b/tools/aapt2/compile/IdAssigner.cpp
index 9a50b26..339b8af 100644
--- a/tools/aapt2/compile/IdAssigner.cpp
+++ b/tools/aapt2/compile/IdAssigner.cpp
@@ -129,11 +129,16 @@
for (auto& type : package->types) {
for (auto& entry : type->entries) {
const ResourceName name(package->name, type->type, entry->name);
- if (entry->id) {
- if (!assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
- context->GetDiagnostics())) {
- return false;
- }
+ if (entry->id && !assigned_ids.ReserveId(name, entry->id.value(), entry->visibility,
+ context->GetDiagnostics())) {
+ return false;
+ }
+
+ auto v = entry->visibility;
+ v.staged_api = true;
+ if (entry->staged_id && !assigned_ids.ReserveId(name, entry->staged_id.value().id, v,
+ context->GetDiagnostics())) {
+ return false;
}
if (assigned_id_map_) {
@@ -237,7 +242,7 @@
if (type_id_ != id.type_id()) {
// Currently there cannot be multiple type ids for a single type.
std::stringstream error;
- error << "type '" << name.type << "' already has ID " << std::hex << (int)id.type_id();
+ error << "type '" << name.type << "' already has ID " << std::hex << (int)type_id_;
return unexpected(error.str());
}
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index f1b350f..2ec01cd 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -254,6 +254,12 @@
}
break;
+ case android::RES_TABLE_STAGED_ALIAS_TYPE:
+ if (!ParseOverlayable(parser.chunk())) {
+ return false;
+ }
+ break;
+
default:
diag_->Warn(DiagMessage(source_)
<< "unexpected chunk type "
@@ -489,6 +495,52 @@
return true;
}
+bool BinaryResourceParser::ParseStagedAliases(const ResChunk_header* chunk) {
+ auto header = ConvertTo<ResTable_staged_alias_header>(chunk);
+ if (!header) {
+ diag_->Error(DiagMessage(source_) << "corrupt ResTable_staged_alias_header chunk");
+ return false;
+ }
+
+ const auto ref_begin = reinterpret_cast<const ResTable_staged_alias_entry*>(
+ ((uint8_t*)header) + util::DeviceToHost32(header->header.headerSize));
+ const auto ref_end = ref_begin + util::DeviceToHost32(header->count);
+ for (auto ref_iter = ref_begin; ref_iter != ref_end; ++ref_iter) {
+ const auto staged_id = ResourceId(util::DeviceToHost32(ref_iter->stagedResId));
+ const auto finalized_id = ResourceId(util::DeviceToHost32(ref_iter->finalizedResId));
+
+ // If the staged alias chunk comes before the type chunks, the resource ids and resource name
+ // pairing will not exist at this point.
+ const auto iter = id_index_.find(finalized_id);
+ if (iter == id_index_.cend()) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource name for finalized"
+ << " resource ID " << finalized_id);
+ return false;
+ }
+
+ // Set the staged if of the finalized resource.
+ const auto& resource_name = iter->second;
+ const StagedId staged_id_def{.id = staged_id};
+ if (!table_->AddResource(NewResourceBuilder(resource_name)
+ .SetId(finalized_id, OnIdConflict::CREATE_ENTRY)
+ .SetStagedId(staged_id_def)
+ .SetAllowMangled(true)
+ .Build(),
+ diag_)) {
+ return false;
+ }
+
+ // Since a the finalized resource entry is cloned and added to the resource table under the
+ // staged resource id, remove the cloned resource entry from the table.
+ if (!table_->RemoveResource(resource_name, staged_id)) {
+ diag_->Error(DiagMessage(source_) << "failed to find resource entry for staged "
+ << " resource ID " << staged_id);
+ return false;
+ }
+ }
+ return true;
+}
+
std::unique_ptr<Item> BinaryResourceParser::ParseValue(const ResourceNameRef& name,
const ConfigDescription& config,
const android::Res_value& value) {
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.h b/tools/aapt2/format/binary/BinaryResourceParser.h
index 13dd982..cd71d16 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.h
+++ b/tools/aapt2/format/binary/BinaryResourceParser.h
@@ -57,6 +57,7 @@
uint8_t package_id);
bool ParseLibrary(const android::ResChunk_header* chunk);
bool ParseOverlayable(const android::ResChunk_header* chunk);
+ bool ParseStagedAliases(const android::ResChunk_header* chunk);
std::unique_ptr<Item> ParseValue(const ResourceNameRef& name,
const android::ConfigDescription& config,
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 74ecf47..a9192e8 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -72,7 +72,7 @@
}
struct FlatEntry {
- const ResourceEntry* entry;
+ const ResourceTableEntryView* entry;
const Value* value;
// The entry string pool index to the entry's name.
@@ -286,6 +286,10 @@
return false;
}
+ if (!FlattenAliases(buffer)) {
+ return false;
+ }
+
pkg_writer.Finish();
return true;
}
@@ -351,8 +355,8 @@
BigBuffer values_buffer(512);
for (FlatEntry& flat_entry : *entries) {
- CHECK(static_cast<size_t>(flat_entry.entry->id.value().entry_id()) < num_total_entries);
- offsets[flat_entry.entry->id.value().entry_id()] = values_buffer.size();
+ CHECK(static_cast<size_t>(flat_entry.entry->id.value()) < num_total_entries);
+ offsets[flat_entry.entry->id.value()] = values_buffer.size();
if (!FlattenValue(&flat_entry, &values_buffer)) {
diag_->Error(DiagMessage()
<< "failed to flatten resource '"
@@ -404,6 +408,26 @@
return true;
}
+ bool FlattenAliases(BigBuffer* buffer) {
+ if (aliases_.empty()) {
+ return true;
+ }
+
+ ChunkWriter alias_writer(buffer);
+ auto header =
+ alias_writer.StartChunk<ResTable_staged_alias_header>(RES_TABLE_STAGED_ALIAS_TYPE);
+ header->count = util::HostToDevice32(aliases_.size());
+
+ auto mapping = alias_writer.NextBlock<ResTable_staged_alias_entry>(aliases_.size());
+ for (auto& p : aliases_) {
+ mapping->stagedResId = util::HostToDevice32(p.first);
+ mapping->finalizedResId = util::HostToDevice32(p.second);
+ ++mapping;
+ }
+ alias_writer.Finish();
+ return true;
+ }
+
bool FlattenOverlayable(BigBuffer* buffer) {
std::set<ResourceId> seen_ids;
std::map<std::string, OverlayableChunk> overlayable_chunks;
@@ -413,18 +437,17 @@
CHECK(bool(type.id)) << "type must have an ID set when flattening <overlayable>";
for (auto& entry : type.entries) {
CHECK(bool(type.id)) << "entry must have an ID set when flattening <overlayable>";
- if (!entry->overlayable_item) {
+ if (!entry.overlayable_item) {
continue;
}
- const OverlayableItem& item = entry->overlayable_item.value();
+ const OverlayableItem& item = entry.overlayable_item.value();
// Resource ids should only appear once in the resource table
- ResourceId id =
- android::make_resid(package_.id.value(), type.id.value(), entry->id.value().entry_id());
+ ResourceId id = android::make_resid(package_.id.value(), type.id.value(), entry.id.value());
CHECK(seen_ids.find(id) == seen_ids.end())
<< "multiple overlayable definitions found for resource "
- << ResourceName(package_.name, type.type, entry->name).to_string();
+ << ResourceName(package_.name, type.type, entry.name).to_string();
seen_ids.insert(id);
// Find the overlayable chunk with the specified name
@@ -452,9 +475,8 @@
if (item.policies == 0) {
context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
- << "overlayable "
- << entry->name
- << " does not specify policy");
+ << "overlayable " << entry.name
+ << " does not specify policy");
return false;
}
@@ -520,7 +542,8 @@
}
bool FlattenTypeSpec(const ResourceTableTypeView& type,
- const std::vector<const ResourceEntry*>& sorted_entries, BigBuffer* buffer) {
+ const std::vector<ResourceTableEntryView>& sorted_entries,
+ BigBuffer* buffer) {
ChunkWriter type_spec_writer(buffer);
ResTable_typeSpec* spec_header =
type_spec_writer.StartChunk<ResTable_typeSpec>(RES_TABLE_TYPE_SPEC_TYPE);
@@ -534,7 +557,7 @@
// We can't just take the size of the vector. There may be holes in the
// entry ID space.
// Since the entries are sorted by ID, the last one will be the biggest.
- const size_t num_entries = sorted_entries.back()->id.value().entry_id() + 1;
+ const size_t num_entries = sorted_entries.back().id.value() + 1;
spec_header->entryCount = util::HostToDevice32(num_entries);
@@ -542,23 +565,23 @@
// show for which configuration axis the resource changes.
uint32_t* config_masks = type_spec_writer.NextBlock<uint32_t>(num_entries);
- for (const ResourceEntry* entry : sorted_entries) {
- const uint16_t entry_id = entry->id.value().entry_id();
+ for (const ResourceTableEntryView& entry : sorted_entries) {
+ const uint16_t entry_id = entry.id.value();
// Populate the config masks for this entry.
uint32_t& entry_config_masks = config_masks[entry_id];
- if (entry->visibility.level == Visibility::Level::kPublic) {
+ if (entry.visibility.level == Visibility::Level::kPublic) {
entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_PUBLIC);
}
- if (entry->visibility.staged_api) {
+ if (entry.visibility.staged_api) {
entry_config_masks |= util::HostToDevice32(ResTable_typeSpec::SPEC_STAGED_API);
}
- const size_t config_count = entry->values.size();
+ const size_t config_count = entry.values.size();
for (size_t i = 0; i < config_count; i++) {
- const ConfigDescription& config = entry->values[i]->config;
+ const ConfigDescription& config = entry.values[i]->config;
for (size_t j = i + 1; j < config_count; j++) {
- config_masks[entry_id] |= util::HostToDevice32(config.diff(entry->values[j]->config));
+ config_masks[entry_id] |= util::HostToDevice32(config.diff(entry.values[j]->config));
}
}
}
@@ -590,7 +613,7 @@
}
// Since the entries are sorted by ID, the last ID will be the largest.
- const size_t num_entries = type.entries.back()->id.value().entry_id() + 1;
+ const size_t num_entries = type.entries.back().id.value() + 1;
// The binary resource table lists resource entries for each
// configuration.
@@ -603,20 +626,26 @@
// hardcoded string uses characters which make it an invalid resource name
const std::string obfuscated_resource_name = "0_resource_name_obfuscated";
- for (const ResourceEntry* entry : type.entries) {
+ for (const ResourceTableEntryView& entry : type.entries) {
+ if (entry.staged_id) {
+ aliases_.insert(std::make_pair(
+ entry.staged_id.value().id.id,
+ ResourceId(package_.id.value(), type.id.value(), entry.id.value()).id));
+ }
+
uint32_t local_key_index;
- ResourceName resource_name({}, type.type, entry->name);
+ ResourceName resource_name({}, type.type, entry.name);
if (!collapse_key_stringpool_ ||
name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
- local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
+ local_key_index = (uint32_t)key_pool_.MakeRef(entry.name).index();
} else {
// resource isn't exempt from collapse, add it as obfuscated value
local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
}
// Group values by configuration.
- for (auto& config_value : entry->values) {
+ for (auto& config_value : entry.values) {
config_to_entry_list_map[config_value->config].push_back(
- FlatEntry{entry, config_value->value.get(), local_key_index});
+ FlatEntry{&entry, config_value->value.get(), local_key_index});
}
}
@@ -667,6 +696,7 @@
StringPool key_pool_;
bool collapse_key_stringpool_;
const std::set<ResourceName>& name_collapse_exemptions_;
+ std::map<uint32_t, uint32_t> aliases_;
};
} // namespace
@@ -684,7 +714,8 @@
});
// Write the ResTable header.
- const auto& table_view = table->GetPartitionedView();
+ const auto& table_view =
+ table->GetPartitionedView(ResourceTableViewOptions{.create_alias_entries = true});
ChunkWriter table_writer(buffer_);
ResTable_header* table_header = table_writer.StartChunk<ResTable_header>(RES_TABLE_TYPE);
table_header->packageCount = util::HostToDevice32(table_view.packages.size());
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index ec331df..236c381 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -498,10 +498,20 @@
out_error)) {
return false;
}
-
entry->overlayable_item = std::move(overlayable_item);
}
+ if (pb_entry.has_staged_id()) {
+ const pb::StagedId& pb_staged_id = pb_entry.staged_id();
+
+ StagedId staged_id;
+ if (pb_staged_id.has_source()) {
+ DeserializeSourceFromPb(pb_staged_id.source(), src_pool, &staged_id.source);
+ }
+ staged_id.id = pb_staged_id.staged_id();
+ entry->staged_id = std::move(staged_id);
+ }
+
ResourceId resid(pb_package.package_id().id(), pb_type.type_id().id(),
pb_entry.entry_id().id());
if (resid.is_valid()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index d2f0336..6042ba8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -364,43 +364,52 @@
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
for (const auto& entry : type.entries) {
pb::Entry* pb_entry = pb_type->add_entry();
- if (entry->id) {
- pb_entry->mutable_entry_id()->set_id(entry->id.value().entry_id());
+ if (entry.id) {
+ pb_entry->mutable_entry_id()->set_id(entry.id.value());
}
- ResourceName resource_name({}, type.type, entry->name);
+ ResourceName resource_name({}, type.type, entry.name);
if (options.collapse_key_stringpool &&
options.name_collapse_exemptions.find(resource_name) ==
options.name_collapse_exemptions.end()) {
pb_entry->set_name(obfuscated_resource_name);
} else {
- pb_entry->set_name(entry->name);
+ pb_entry->set_name(entry.name);
}
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
- pb_visibility->set_staged_api(entry->visibility.staged_api);
- pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
+ pb_visibility->set_staged_api(entry.visibility.staged_api);
+ pb_visibility->set_level(SerializeVisibilityToPb(entry.visibility.level));
if (source_pool != nullptr) {
- SerializeSourceToPb(entry->visibility.source, source_pool.get(),
+ SerializeSourceToPb(entry.visibility.source, source_pool.get(),
pb_visibility->mutable_source());
}
- pb_visibility->set_comment(entry->visibility.comment);
+ pb_visibility->set_comment(entry.visibility.comment);
- if (entry->allow_new) {
+ if (entry.allow_new) {
pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
if (source_pool != nullptr) {
- SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
+ SerializeSourceToPb(entry.allow_new.value().source, source_pool.get(),
pb_allow_new->mutable_source());
}
- pb_allow_new->set_comment(entry->allow_new.value().comment);
+ pb_allow_new->set_comment(entry.allow_new.value().comment);
}
- if (entry->overlayable_item) {
- SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
+ if (entry.overlayable_item) {
+ SerializeOverlayableItemToPb(entry.overlayable_item.value(), overlayables,
source_pool.get(), pb_entry, out_table);
}
- for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
+ if (entry.staged_id) {
+ pb::StagedId* pb_staged_id = pb_entry->mutable_staged_id();
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(entry.staged_id.value().source, source_pool.get(),
+ pb_staged_id->mutable_source());
+ }
+ pb_staged_id->set_staged_id(entry.staged_id.value().id.id);
+ }
+
+ for (const ResourceConfigValue* config_value : entry.values) {
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
SerializeConfig(config_value->config, pb_config_value->mutable_config());
pb_config_value->mutable_config()->set_product(config_value->product);
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index e563eda..38c811f 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -928,4 +928,27 @@
EXPECT_THAT(deserialized->alias_namespaces, Eq(original->alias_namespaces));
}
+TEST(ProtoSerializeTest, StagedId) {
+ CloningValueTransformer cloner(nullptr);
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+ .Add(NewResourceBuilder("com.app.a:string/foo")
+ .SetStagedId(StagedId{.id = 0x01ff0001})
+ .Build())
+ .Build();
+
+ ResourceTable new_table;
+ pb::ResourceTable pb_table;
+ MockFileCollection files;
+ std::string error;
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error));
+ EXPECT_THAT(error, IsEmpty());
+
+ auto result = new_table.FindResource(test::ParseNameOrDie("com.app.a:string/foo"));
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(result.value().entry->staged_id);
+ EXPECT_THAT(result.value().entry->staged_id.value().id, Eq(ResourceId(0x01ff0001)));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index bc93ec6..22f4d18 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -151,6 +151,18 @@
dst_entry->overlayable_item = std::move(src_entry->overlayable_item);
}
+ if (src_entry->staged_id) {
+ if (dst_entry->staged_id &&
+ dst_entry->staged_id.value().id != src_entry->staged_id.value().id) {
+ context->GetDiagnostics()->Error(DiagMessage(src_entry->staged_id.value().source)
+ << "conflicting staged id declaration for resource '"
+ << src_entry->name << "'");
+ context->GetDiagnostics()->Error(DiagMessage(dst_entry->staged_id.value().source)
+ << "previous declaration here");
+ }
+ dst_entry->staged_id = std::move(src_entry->staged_id);
+ }
+
return true;
}
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index f94f0fe..285e5a1 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -80,9 +80,6 @@
}
void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
- CHECK(util::StartsWith(path, temp_dir_))
- << "Attempting to create a file outside of test temporary directory.";
-
// Create any intermediate directories specified in the path
auto pos = std::find(path.rbegin(), path.rend(), file::sDirSep);
if (pos != path.rend()) {