diff options
713 files changed, 16755 insertions, 7889 deletions
diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java index 303c667351d1..2b7af2f0a11e 100644 --- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java +++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java @@ -23,11 +23,14 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertTrue; + import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.inputmethodservice.InputMethodService; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.SystemClock; import android.perftests.utils.ManualBenchmarkState; import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest; @@ -88,7 +91,7 @@ public class ImePerfTest extends ImePerfTestBase "ISC.onPostLayout" }; - /** IMF show methods to log in trace. */ + /** IMF show methods to log in trace. */ private String[] mShowMethods = { "IC.showRequestFromIme", "IC.showRequestFromApi", @@ -99,7 +102,23 @@ public class ImePerfTest extends ImePerfTestBase "IMMS.showSoftInput", "IMS.showSoftInput", "IMS.startInput", - "WMS.showImePostLayout" + "WMS.showImePostLayout", + "IMS.updateFullscreenMode", + "IMS.onComputeInsets", + "IMS.showWindow" + }; + + /** IMF show methods to log in trace. */ + private String[] mShowMethodsCold = { + "IMS.bindInput", + "IMS.initializeInternal", + "IMS.restartInput", + "IMS.onCreate", + "IMS.initSoftInputWindow", + "IMS.resetStateForNewConfiguration", + "IMMS.onServiceConnected", + "IMMS.sessionCreated", + "IMMS.startInputOrWindowGainedFocus" }; /** IMF hide lifecycle methods to log in trace. */ @@ -163,6 +182,7 @@ public class ImePerfTest extends ImePerfTestBase public static class BaselineIme extends InputMethodService { public static final int HEIGHT_DP = 100; + private static int sPid; @Override public View onCreateInputView() { @@ -173,9 +193,14 @@ public class ImePerfTest extends ImePerfTestBase view.setPadding(0, 0, 0, 0); view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height)); inner.setBackgroundColor(0xff01fe10); // green + sPid = Process.myPid(); return view; } + static int getPid() { + return sPid; + } + static ComponentName getName(Context context) { return new ComponentName(context, BaselineIme.class); } @@ -188,8 +213,8 @@ public class ImePerfTest extends ImePerfTestBase flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR)) - public void testShowIme() throws Throwable { - testShowOrHideIme(true /* show */); + public void testShowImeWarm() throws Throwable { + testShowOrHideImeWarm(true /* show */); } @Test @@ -200,10 +225,65 @@ public class ImePerfTest extends ImePerfTestBase | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR)) public void testHideIme() throws Throwable { - testShowOrHideIme(false /* show */); + testShowOrHideImeWarm(false /* show */); + } + + @Test + @ManualBenchmarkTest( + targetTestDurationNs = 10 * TIME_1_S_IN_NS, + statsReport = @StatsReport( + flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN + | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX + | StatsReport.FLAG_COEFFICIENT_VAR)) + public void testShowImeCold() throws Throwable { + mTraceMethods = new TraceMarkParser( + buildArray(mCommonMethods, mShowMethods, mShowMethodsCold)); + + final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + state.setCustomizedIterations(getProfilingIterations(), this); + if (state.isWarmingUp()) { + // we don't need to warmup for cold start. + return; + } + + long measuredTimeNs = 0; + while (state.keepRunning(measuredTimeNs)) { + killBaselineIme(); + try (ImeSession imeSession = new ImeSession(BaselineIme.getName( + getInstrumentation().getContext()))) { + final AtomicReference<CountDownLatch> latchStart = new AtomicReference<>(); + final Activity activity = getActivityWithFocus(); + + setImeListener(activity, latchStart, null /* latchEnd */); + latchStart.set(new CountDownLatch(1)); + + if (!mIsTraceStarted) { + startAsyncAtrace(); + } + + final WindowInsetsController controller = + activity.getWindow().getDecorView().getWindowInsetsController(); + AtomicLong startTime = new AtomicLong(); + activity.runOnUiThread(() -> { + startTime.set(SystemClock.elapsedRealtimeNanos()); + controller.show(WindowInsets.Type.ime()); + }); + + measuredTimeNs = waitForAnimationStart(latchStart, startTime); + mActivityRule.finishActivity(); + } + } + stopAsyncAtrace(); + addResultToState(state); + } + + private void killBaselineIme() { + assertTrue("PID of test and IME can't be same", + Process.myPid() != BaselineIme.getPid()); + Process.killProcess(BaselineIme.getPid()); } - private void testShowOrHideIme(final boolean show) throws Throwable { + private void testShowOrHideImeWarm(final boolean show) throws Throwable { mTraceMethods = new TraceMarkParser(buildArray( mCommonMethods, show ? mShowMethods : mHideMethods)); final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index e57359fbf50e..5fd45eadbda9 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -141,6 +141,25 @@ public class AppSearchManager { } /** + * Creates a new {@link GlobalSearchSession}. + * + * <p>This process requires an AppSearch native indexing file system for each user. If it's not + * created for this user, the initialization process will create one under user's directory. + * + * @param executor Executor on which to invoke the callback. + * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of + * performing this operation. Or a {@link AppSearchResult} with failure + * reason code and error information. + */ + public void createGlobalSearchSession( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) { + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + GlobalSearchSession.createGlobalSearchSession(mService, executor, callback); + } + + /** * Sets the schema being used by documents provided to the {@link #putDocuments} method. * * <p>The schema provided here is compared to the stored copy of the schema previously supplied @@ -383,22 +402,26 @@ public class AppSearchManager { @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending // them in one big list. - AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>(); + AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { - mService.query(DEFAULT_DATABASE_NAME, queryExpression, - searchSpec.getBundle(), searchResultsFuture); + mService.query(DEFAULT_DATABASE_NAME, queryExpression, searchSpec.getBundle(), + new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + future.complete(result); + } + }); + AppSearchResult<Bundle> bundleResult = getFutureOrThrow(future); + if (!bundleResult.isSuccess()) { + return AppSearchResult.newFailedResult(bundleResult.getResultCode(), + bundleResult.getErrorMessage()); + } + SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue()); + return AppSearchResult.newSuccessfulResult(searchResultPage.getResults()); } catch (RemoteException e) { - searchResultsFuture.completeExceptionally(e); - } - - // Translate the Bundle into a searchResultPage. - AppSearchResult<Bundle> bundleResult = getFutureOrThrow(searchResultsFuture); - if (!bundleResult.isSuccess()) { - return AppSearchResult.newFailedResult(bundleResult.getResultCode(), - bundleResult.getErrorMessage()); + throw e.rethrowFromSystemServer(); + } catch (Throwable t) { + return AppSearchResult.throwableToFailedResult(t); } - SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue()); - return AppSearchResult.newSuccessfulResult(searchResultPage.getResults()); } /** diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl index f0b29964b895..37ce990b97c4 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.aidl @@ -16,4 +16,4 @@ package android.app.appsearch; /** {@hide} */ -parcelable AppSearchResult;
\ No newline at end of file +parcelable AppSearchResult<ValueType>;
\ No newline at end of file diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 531436ecaf0c..9c7ccea4c43b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -270,6 +270,61 @@ public final class AppSearchSession { } /** + * Searches a document based on a given query string. + * + * <p>Currently we support following features in the raw query format: + * <ul> + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and + * ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or + * ‘cat’”). + * Example: dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do + * not have the term ‘dog’”). + * Example: -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p> Specifies which properties of a document to specifically match terms in (e.g. + * “match documents where the ‘subject’ property contains ‘important’”). + * Example: subject:important matches documents with the term ‘important’ in the + * ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level document + * fields, such as schema_type. Clients should be able to limit their query to documents of + * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). + * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents + * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the + * ‘Video’ schema type. + * </ul> + * + * <p> This method is lightweight. The heavy work will be done in + * {@link SearchResults#getNextPage}. + * + * @param queryExpression Query String to search. + * @param searchSpec Spec for setting filters, raw query etc. + * @param executor Executor on which to invoke the callback of the following request + * {@link SearchResults#getNextPage}. + * @return The search result of performing this operation. + */ + @NonNull + public SearchResults query( + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor) { + Objects.requireNonNull(queryExpression); + Objects.requireNonNull(searchSpec); + Objects.requireNonNull(executor); + return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, executor); + } + + /** * Removes {@link GenericDocument}s from the index by URI. * * @param request Request containing URIs to be removed. @@ -307,6 +362,39 @@ public final class AppSearchSession { } } - // TODO(b/162450968) port query() and SearchResults.java to platform. - // TODO(b/162450968) port removeByQuery() to platform. + /** + * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they + * match the {@code queryExpression} in given namespaces and schemaTypes which is set via + * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchema}. + * + * <p> An empty {@code queryExpression} matches all documents. + * + * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in + * the current database. + * + * @param queryExpression Query String to search. + * @param searchSpec Defines what and how to remove + * @param executor Executor on which to invoke the callback. + * @param callback Callback to receive errors resulting from removing the documents. If the + * operation succeeds, the callback will be invoked with {@code null}. + */ + public void removeByQuery(@NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<Void>> callback) { + Objects.requireNonNull(queryExpression); + Objects.requireNonNull(searchSpec); + Objects.requireNonNull(executor); + Objects.requireNonNull(callback); + try { + mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), + new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + executor.execute(() -> callback.accept(result)); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java new file mode 100644 index 000000000000..d2aa8eab4708 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.os.RemoteException; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * This class provides global access to the centralized AppSearch index maintained by the system. + * + * <p>Apps can retrieve indexed documents through the query API. + * @hide + */ +public class GlobalSearchSession { + + private final IAppSearchManager mService; + + static void createGlobalSearchSession( + @NonNull IAppSearchManager service, + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) { + GlobalSearchSession globalSearchSession = new GlobalSearchSession(service); + globalSearchSession.initialize(executor, callback); + } + + // NOTE: No instance of this class should be created or returned except via initialize(). + // Once the callback.accept has been called here, the class is ready to use. + private void initialize( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) { + try { + mService.initialize(new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + executor.execute(() -> { + if (result.isSuccess()) { + callback.accept( + AppSearchResult.newSuccessfulResult(GlobalSearchSession.this)); + } else { + callback.accept(result); + } + }); + } + }); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + private GlobalSearchSession(@NonNull IAppSearchManager service) { + mService = service; + } + + /** + * Searches across all documents in the storage based on a given query string. + * + * <p>Currently we support following features in the raw query format: + * <ul> + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and + * ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or + * ‘cat’”). + * Example: dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do + * not have the term ‘dog’”). + * Example: -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p> Specifies which properties of a document to specifically match terms in (e.g. + * “match documents where the ‘subject’ property contains ‘important’”). + * Example: subject:important matches documents with the term ‘important’ in the + * ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level document + * fields, such as schema_type. Clients should be able to limit their query to documents of + * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). + * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents + * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the + * ‘Video’ schema type. + * </ul> + * + * <p> This method is lightweight. The heavy work will be done in + * {@link SearchResults#getNextPage}. + * + * @param queryExpression Query String to search. + * @param searchSpec Spec for setting filters, raw query etc. + * @param executor Executor on which to invoke the callback of the following request + * {@link SearchResults#getNextPage}. + * @return The search result of performing this operation. + */ + @NonNull + public SearchResults globalQuery( + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor) { + Objects.requireNonNull(queryExpression); + Objects.requireNonNull(searchSpec); + Objects.requireNonNull(executor); + return new SearchResults(mService, /*databaseName=*/null, queryExpression, + searchSpec, executor); + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 4a981022da73..22e00f2cdfdc 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -85,13 +85,46 @@ interface IAppSearchManager { * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link SearchResults}>> + * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this + * operation. */ void query( in String databaseName, in String queryExpression, in Bundle searchSpecBundle, - in AndroidFuture<AppSearchResult> callback); + in IAppSearchResultCallback callback); + + /** + * Executes a global query, i.e. over all permitted databases, against the AppSearch index and + * returns results. + * + * @param queryExpression String to search for + * @param searchSpecBundle SearchSpec bundle + * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this + * operation. + */ + void globalQuery( + in String queryExpression, + in Bundle searchSpecBundle, + in IAppSearchResultCallback callback); + + /** + * Fetches the next page of results of a previously executed query. Results can be empty if + * next-page token is invalid or all pages have been returned. + * + * @param nextPageToken The token of pre-loaded results of previously executed query. + * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this + * operation. + */ + void getNextPage(in long nextPageToken, in IAppSearchResultCallback callback); + + /** + * Invalidates the next-page token so that no more results of the related query can be returned. + * + * @param nextPageToken The token of pre-loaded results of previously executed query to be + * Invalidated. + */ + void invalidateNextPageToken(in long nextPageToken); /** * Removes documents by URI. @@ -119,13 +152,14 @@ interface IAppSearchManager { * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link SearchResults}>> + * @param callback {@link IAppSearchResultCallback#onResult} will be called with an + * {@link AppSearchResult}<{@link Void}>. */ void removeByQuery( in String databaseName, in String queryExpression, in Bundle searchSpecBundle, - in AndroidFuture<AppSearchResult> callback); + in IAppSearchResultCallback callback); /** * Creates and initializes AppSearchImpl for the calling app. diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index 9f376250f1a6..8548d209c787 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -16,63 +16,123 @@ package android.app.appsearch; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; -import java.util.ArrayList; +import com.android.internal.util.Preconditions; + +import java.io.Closeable; import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Consumer; /** - * Structure for transmitting a page of search results across binder. + * SearchResults are a returned object from a query API. + * + * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets + * based on request. + * + * <p>Should close this object after finish fetching results. + * + * <p>This class is not thread safe. * @hide */ -public final class SearchResults implements Parcelable { - final List<SearchResult> mResults; - final long mNextPageToken; +public class SearchResults implements Closeable { + private static final String TAG = "SearchResults"; + + private final IAppSearchManager mService; + + @Nullable + private final String mDatabaseName; + + private final String mQueryExpression; + + private final SearchSpec mSearchSpec; + + private final Executor mExecutor; - public SearchResults(@NonNull List<SearchResult> results, long nextPageToken) { - mResults = results; - mNextPageToken = nextPageToken; + private long mNextPageToken; + + private boolean mIsFirstLoad = true; + + SearchResults(@NonNull IAppSearchManager service, + @Nullable String databaseName, + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec, + @NonNull @CallbackExecutor Executor executor) { + mService = Preconditions.checkNotNull(service); + mExecutor = Preconditions.checkNotNull(executor); + mDatabaseName = databaseName; + mQueryExpression = Preconditions.checkNotNull(queryExpression); + mSearchSpec = Preconditions.checkNotNull(searchSpec); } - private SearchResults(@NonNull Parcel in) { - List<Bundle> resultBundles = in.readArrayList(/*loader=*/ null); - mResults = new ArrayList<>(resultBundles.size()); - for (int i = 0; i < resultBundles.size(); i++) { - SearchResult searchResult = new SearchResult(resultBundles.get(i)); - mResults.add(searchResult); + /** + * Gets a whole page of {@link SearchResult}s. + * + * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an + * empty list. + * + * <p>The page size is set by {@link SearchSpec.Builder#setNumPerPage}. + * + * @param callback Callback to receive the pending result of performing this operation. + */ + public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { + try { + if (mIsFirstLoad) { + mIsFirstLoad = false; + if (mDatabaseName == null) { + mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(), + wrapCallback(callback)); + } else { + mService.query(mDatabaseName, mQueryExpression, mSearchSpec.getBundle(), + wrapCallback(callback)); + } + } else { + mService.getNextPage(mNextPageToken, wrapCallback(callback)); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - mNextPageToken = in.readLong(); } - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - List<Bundle> resultBundles = new ArrayList<>(mResults.size()); - for (int i = 0; i < mResults.size(); i++) { - resultBundles.add(mResults.get(i).getBundle()); + private void invokeCallback(AppSearchResult result, + @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { + if (result.isSuccess()) { + try { + SearchResultPage searchResultPage = + new SearchResultPage((Bundle) result.getResultValue()); + mNextPageToken = searchResultPage.getNextPageToken(); + callback.accept(AppSearchResult.newSuccessfulResult( + searchResultPage.getResults())); + } catch (Throwable t) { + callback.accept(AppSearchResult.throwableToFailedResult(t)); + } + } else { + callback.accept(result); } - dest.writeList(resultBundles); - dest.writeLong(mNextPageToken); } - @Override - public int describeContents() { - return 0; + public void close() { + mExecutor.execute(() -> { + try { + mService.invalidateNextPageToken(mNextPageToken); + } catch (RemoteException e) { + Log.d(TAG, "Unable to close the SearchResults", e); + } + }); } - public static final Creator<SearchResults> CREATOR = new Creator<SearchResults>() { - @NonNull - @Override - public SearchResults createFromParcel(@NonNull Parcel in) { - return new SearchResults(in); - } - - @NonNull - @Override - public SearchResults[] newArray(int size) { - return new SearchResults[size]; - } - }; + private IAppSearchResultCallback wrapCallback( + @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { + return new IAppSearchResultCallback.Stub() { + public void onResult(AppSearchResult result) { + mExecutor.execute(() -> invokeCallback(result, callback)); + } + }; + } } 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 8269799d5a0d..d5146dd75c3b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -36,7 +36,6 @@ import android.os.UserHandle; import android.util.ArraySet; import android.util.Log; -import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; @@ -69,6 +68,7 @@ public class AppSearchManagerService extends SystemService { @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(schemaBundles); + Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); @@ -158,13 +158,12 @@ public class AppSearchManagerService extends SystemService { } // TODO(sidchhabra): Do this in a threadpool. - // TODO(b/162450968) handle pagination after getNextPage and SearchResults is ready. @Override public void query( @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, - @NonNull AndroidFuture<AppSearchResult> callback) { + @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); @@ -179,10 +178,70 @@ public class AppSearchManagerService extends SystemService { databaseName, queryExpression, new SearchSpec(searchSpecBundle)); - callback.complete( + invokeCallbackOnResult(callback, + AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); + } catch (Throwable t) { + invokeCallbackOnError(callback, t); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + public void globalQuery( + @NonNull String queryExpression, + @NonNull Bundle searchSpecBundle, + @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(queryExpression); + Preconditions.checkNotNull(searchSpecBundle); + Preconditions.checkNotNull(callback); + int callingUid = Binder.getCallingUidOrThrow(); + int callingUserId = UserHandle.getUserId(callingUid); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + SearchResultPage searchResultPage = impl.globalQuery( + queryExpression, + new SearchSpec(searchSpecBundle)); + invokeCallbackOnResult(callback, + AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); + } catch (Throwable t) { + invokeCallbackOnError(callback, t); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override + public void getNextPage(long nextPageToken, + @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(callback); + int callingUid = Binder.getCallingUidOrThrow(); + int callingUserId = UserHandle.getUserId(callingUid); + final long callingIdentity = Binder.clearCallingIdentity(); + // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally + // opened it + try { + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); + invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); } catch (Throwable t) { - callback.complete(throwableToFailedResult(t)); + invokeCallbackOnError(callback, t); + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + + @Override + public void invalidateNextPageToken(long nextPageToken) { + int callingUid = Binder.getCallingUidOrThrow(); + int callingUserId = UserHandle.getUserId(callingUid); + final long callingIdentity = Binder.clearCallingIdentity(); + try { + AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); + impl.invalidateNextPageToken(nextPageToken); + } catch (Throwable t) { + Log.d(TAG, "Unable to invalidate the query page token", t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -225,7 +284,7 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, - @NonNull AndroidFuture<AppSearchResult> callback) { + @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); @@ -236,10 +295,11 @@ public class AppSearchManagerService extends SystemService { try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle)); - callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null)); + impl.removeByQuery(databaseName, queryExpression, + new SearchSpec(searchSpecBundle)); + invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { - callback.complete(throwableToFailedResult(t)); + invokeCallbackOnError(callback, t); } finally { Binder.restoreCallingIdentity(callingIdentity); } @@ -247,6 +307,7 @@ public class AppSearchManagerService extends SystemService { @Override public void initialize(@NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 2c8a5589ca51..3597c27224bb 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -462,11 +462,11 @@ public class JobInfo implements Parcelable { public @NetworkType int getNetworkType() { if (networkRequest == null) { return NETWORK_TYPE_NONE; - } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) { + } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_METERED)) { return NETWORK_TYPE_UNMETERED; - } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { + } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_ROAMING)) { return NETWORK_TYPE_NOT_ROAMING; - } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { + } else if (networkRequest.hasTransport(TRANSPORT_CELLULAR)) { return NETWORK_TYPE_CELLULAR; } else { return NETWORK_TYPE_ANY; @@ -1558,7 +1558,7 @@ public class JobInfo implements Parcelable { if (isPersisted) { // We can't serialize network specifiers if (networkRequest != null - && networkRequest.networkCapabilities.getNetworkSpecifier() != null) { + && networkRequest.getNetworkSpecifier() != null) { throw new IllegalArgumentException( "Network specifiers aren't supported for persistent jobs"); } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d4ea7af06ed1..20c77da53f2c 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -417,6 +417,9 @@ public class AlarmManagerService extends SystemService { @VisibleForTesting static final String KEY_LAZY_BATCHING = "lazy_batching"; + private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE = + "time_tick_allowed_while_idle"; + private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS; @@ -440,6 +443,7 @@ public class AlarmManagerService extends SystemService { private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY; private static final boolean DEFAULT_LAZY_BATCHING = true; + private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; @@ -470,6 +474,7 @@ public class AlarmManagerService extends SystemService { public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW; public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING; + public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE; private long mLastAllowWhileIdleWhitelistDuration = -1; @@ -557,6 +562,11 @@ public class AlarmManagerService extends SystemService { migrateAlarmsToNewStoreLocked(); } break; + case KEY_TIME_TICK_ALLOWED_WHILE_IDLE: + TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean( + KEY_TIME_TICK_ALLOWED_WHILE_IDLE, + DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE); + break; default: if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) { // The quotas need to be updated in order, so we can't just rely @@ -690,6 +700,9 @@ public class AlarmManagerService extends SystemService { pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING); pw.println(); + pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE); + pw.println(); + pw.decreaseIndent(); } @@ -3756,9 +3769,14 @@ public class AlarmManagerService extends SystemService { final long tickEventDelay = nextTime - currentTime; final WorkSource workSource = null; // Let system take blame for time tick events. + + int flags = AlarmManager.FLAG_STANDALONE; + flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED + : 0; + setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, - 0, null, mTimeTickTrigger, TIME_TICK_TAG, AlarmManager.FLAG_STANDALONE, - workSource, null, Process.myUid(), "android"); + 0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null, + Process.myUid(), "android"); // Finally, remember when we set the tick alarm synchronized (mLock) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index 56b97f2e5757..2b5aab83463b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -282,12 +282,13 @@ public final class TimeController extends StateController { String nextDelayPackageName = null; boolean ready = false; Iterator<JobStatus> it = mTrackedJobs.iterator(); + final long nowElapsedMillis = sElapsedRealtimeClock.millis(); while (it.hasNext()) { final JobStatus job = it.next(); if (!job.hasTimingDelayConstraint()) { continue; } - if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) { + if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { if (canStopTrackingJobLocked(job)) { it.remove(); } diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index c2ee6dcd13b2..ef1e413a3112 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -101,8 +101,6 @@ public final class Sm { runFstrim(); } else if ("set-virtual-disk".equals(op)) { runSetVirtualDisk(); - } else if ("set-isolated-storage".equals(op)) { - runIsolatedStorage(); } else if ("start-checkpoint".equals(op)) { runStartCheckpoint(); } else if ("supports-checkpoint".equals(op)) { @@ -286,28 +284,6 @@ public final class Sm { StorageManager.DEBUG_VIRTUAL_DISK); } - public void runIsolatedStorage() throws RemoteException { - final int value; - final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON - | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; - switch (nextArg()) { - case "on": - case "true": - value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON; - break; - case "off": - value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; - break; - case "default": - case "false": - value = 0; - break; - default: - return; - } - mSm.setDebugFlags(value, mask); - } - public void runIdleMaint() throws RemoteException { final boolean im_run = "run".equals(nextArg()); if (im_run) { @@ -367,8 +343,6 @@ public final class Sm { System.err.println(""); System.err.println(" sm set-emulate-fbe [true|false]"); System.err.println(""); - System.err.println(" sm set-isolated-storage [on|off|default]"); - System.err.println(""); System.err.println(" sm start-checkpoint <num-retries>"); System.err.println(""); System.err.println(" sm supports-checkpoint"); diff --git a/cmds/statsd/OWNERS b/cmds/statsd/OWNERS index a61babf32e58..4e4e11988b62 100644 --- a/cmds/statsd/OWNERS +++ b/cmds/statsd/OWNERS @@ -1,9 +1 @@ -jeffreyhuang@google.com -joeo@google.com -jtnguyen@google.com -muhammadq@google.com -ruchirr@google.com -singhtejinder@google.com -tsaichristine@google.com -yaochen@google.com -yro@google.com +baligh@google.com diff --git a/cmds/statsd/src/OWNERS b/cmds/statsd/src/OWNERS deleted file mode 100644 index 0f3ddf7388d9..000000000000 --- a/cmds/statsd/src/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Temporary OWNERS Block to assist with migration -# bug: 167962588 -per-file *atoms.proto = set noparent -per-file *atom_field_options.proto = set noparent -per-file *atoms.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com -per-file *atom_field_options.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 672d61c82268..d355146c13ef 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -85,16 +85,14 @@ public: const std::vector<sp<ConditionTracker>>& allConditions, const vector<Matcher>& dimensions) const override; - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { if (mSlicedChildren.size() == 1) { - return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( - allConditions, dimensions); + return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions); } + return nullptr; } - private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 5a6b8cf334eb..7af022a5677a 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -135,9 +135,8 @@ public: return mProtoHash; } - virtual void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const = 0; + virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const = 0; virtual bool IsChangedDimensionTrackable() const = 0; diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 892647910d9f..43db94cfa47d 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -53,9 +53,9 @@ public: ConditionState getUnSlicedPartConditionState(const int index) { return mAllConditions[index]->getUnSlicedPartConditionState(); } - void getTrueSlicedDimensions(const int index, - std::set<HashableDimensionKey>* trueDimensions) const { - return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); + + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const { + return mAllConditions[index]->getSlicedDimensionMap(mAllConditions); } private: diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 7a8b40108448..2fbc8a7b4ea8 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -74,14 +74,9 @@ public: } } - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { - for (const auto& itr : mSlicedConditionState) { - if (itr.second > 0) { - dimensions->insert(itr.first); - } - } + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { + return &mSlicedConditionState; } bool IsChangedDimensionTrackable() const override { return true; } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 8869241ab8aa..bbaa20f232a9 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -67,8 +67,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, @@ -140,6 +140,8 @@ DurationMetricProducer::DurationMetricProducer( mCurrentBucketStartTimeNs = startTimeNs; VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs); + + initTrueDimensions(whatIndex, startTimeNs); } DurationMetricProducer::~DurationMetricProducer() { @@ -224,6 +226,23 @@ bool DurationMetricProducer::onConfigUpdatedLocked( return true; } +void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating + // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that + // scenario, -1 can also be passed. + if (whatIndex == -1) { + return; + } + const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex); + for (const auto& [internalDimKey, count] : *slicedWhatMap) { + for (int i = 0; i < count; i++) { + // Fake start events. + handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs); + } + } +} + sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { std::lock_guard<std::mutex> lock(mMutex); @@ -330,14 +349,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio // state based on the new unsliced condition state. if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { - std::set<HashableDimensionKey> trueConditionDimensions; - mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); + const map<HashableDimensionKey, int>* slicedConditionMap = + mWizard->getSlicedDimensionMap(mConditionTrackerIndex); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { HashableDimensionKey linkedConditionDimensionKey; getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], &linkedConditionDimensionKey); - if (trueConditionDimensions.find(linkedConditionDimensionKey) != - trueConditionDimensions.end()) { + const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey); + if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) { whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); } } @@ -595,8 +614,9 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey } void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, - bool condition, const LogEvent& event) { + const ConditionKey& conditionKeys, bool condition, + const int64_t eventTimeNs, + const vector<FieldValue>& eventValues) { const auto& whatKey = eventKey.getDimensionKeyInWhat(); auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { @@ -608,20 +628,17 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); if (mUseWhatDimensionAsInternalDimension) { - it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); + it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys); return; } if (mInternalDimensions.empty()) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), - conditionKeys); + it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys); } else { HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; - filterValues(mInternalDimensions, event.getValues(), &dimensionKey); - it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(), - conditionKeys); + filterValues(mInternalDimensions, eventValues, &dimensionKey); + it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys); } - } void DurationMetricProducer::onMatchedLogEventInternalLocked( @@ -633,26 +650,32 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); + handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(), + event.GetElapsedTimestampNs()); +} + +void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const vector<FieldValue>& values, + const int64_t eventTimeNs) { if (eventTimeNs < mTimeBaseNs) { return; } if (mIsActive) { - flushIfNeededLocked(event.GetElapsedTimestampNs()); + flushIfNeededLocked(eventTimeNs); } // Handles Stopall events. if ((int)matcherIndex == mStopAllIndex) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); + whatIt.second->noteStopAll(eventTimeNs); } return; } HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; if (!mDimensionsInWhat.empty()) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); + filterValues(mDimensionsInWhat, values, &dimensionInWhat); } // Stores atom id to primary key pairs for each state atom that the metric is @@ -663,8 +686,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, // field values from the log event. These values will form a primary key // that will be used to query StateTracker for the correct state value. for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); + getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]); } // For each sliced state, query StateTracker for the state value using @@ -695,19 +717,19 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, if (mUseWhatDimensionAsInternalDimension) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false); } return; } HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; if (!mInternalDimensions.empty()) { - filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); + filterValues(mInternalDimensions, values, &internalDimensionKey); } auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false); } return; } @@ -716,7 +738,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, ConditionKey conditionKey; if (mConditionSliced) { for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); + getDimensionForCondition(values, link, &conditionKey[link.conditionId]); } auto conditionState = @@ -731,7 +753,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, condition = condition && mIsActive; handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition, - event); + eventTimeNs, values); } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 5feb09fc1c98..9fb586f7262f 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -40,8 +40,8 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer( const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, @@ -74,8 +74,15 @@ protected: const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; private: + // Initializes true dimensions of the 'what' predicate. Only to be called during initialization. + void initTrueDimensions(const int whatIndex, const int64_t startTimeNs); + + void handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const std::vector<FieldValue>& values, + const int64_t eventTimeNs); void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, - bool condition, const LogEvent& event); + bool condition, const int64_t eventTimeNs, + const vector<FieldValue>& eventValues); void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 4474df4346cf..ff7938cc7243 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -450,7 +450,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - const Predicate& durationWhat = config.predicate(what_it->second); + const int whatIndex = what_it->second; + const Predicate& durationWhat = config.predicate(whatIndex); if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); return nullopt; @@ -536,10 +537,11 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - return {new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex, - nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; + return {new DurationMetricProducer(key, metric, conditionIndex, initialConditionCache, + whatIndex, startIndex, stopIndex, stopAllIndex, nesting, + wizard, metricHash, internalDimensions, timeBaseNs, + currentTimeNs, eventActivationMap, eventDeactivationMap, + slicedStateAtoms, stateGroupMap)}; } optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp index e01a0b63a0ca..22c4f5de2537 100644 --- a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp @@ -14,8 +14,6 @@ #include <gtest/gtest.h> -#include <thread> // std::this_thread::sleep_for - #include "android-base/stringprintf.h" #include "src/StatsLogProcessor.h" #include "src/storage/StorageManager.h" @@ -28,6 +26,7 @@ namespace statsd { #ifdef __ANDROID__ #define STATS_DATA_DIR "/data/misc/stats-data" using android::base::StringPrintf; +using namespace std; namespace { @@ -298,6 +297,300 @@ TEST_P(ConfigUpdateE2eTest, TestConfigTtl) { &buffer); } +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhat) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *config.add_predicate() = holdingWakelockPredicate; + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + // Create a wakelock acquire, causing the condition to be true. + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + durationMetric->set_bucket(FIVE_MINUTES); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 1:20 + processor->OnLogEvent(event.get()); + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 1); + DurationMetricData data = metricData.data(0); + ASSERT_EQ(data.bucket_info_size(), 1); + + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs); + EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs); + EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC); +} + +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + *config.add_predicate() = isInBackgroundPredicate; + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 0:35 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 2); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC); +} + +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + auto uidProcessState = CreateUidProcessState(); + *config.add_state() = uidProcessState; + + // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted. + CountMetric* countMetric = config.add_count_metric(); + countMetric->set_id(StringToId("Tmp")); + countMetric->set_what(config.atom_matcher(0).id()); + countMetric->add_slice_by_state(uidProcessState.id()); + // The metric is dimensioning by first attribution node and only by uid. + *countMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + countMetric->set_bucket(FIVE_MINUTES); + auto stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + config.add_no_report_metric(countMetric->id()); + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->add_slice_by_state(uidProcessState.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + stateLink = durationMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 1:13 + processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 3); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC); + + data = metricData.data(2); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index d1f89775ed6a..bb2ede4ddc2b 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -73,9 +73,9 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, durationProducer.mCurrentBucketNum); @@ -101,9 +101,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -145,8 +145,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); @@ -195,8 +196,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -240,9 +242,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -303,9 +305,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -367,9 +369,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -413,9 +415,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -467,9 +469,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index 9e2350b33018..88df2ff0caa8 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -875,8 +875,9 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { FieldMatcher dimensions; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)}); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, 0x0123456789, dimensions, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); } diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 1761d5d9e1fa..153b696808dc 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -1091,6 +1091,21 @@ void ValidateAttributionUidAndTagDimension( .value_tuple().dimensions_value(1).value_str(), tag); } +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value) { + ASSERT_EQ(stateValues.size(), 1); + ASSERT_EQ(stateValues[0].atom_id(), atomId); + switch (stateValues[0].contents_case()) { + case StateValue::ContentsCase::kValue: + EXPECT_EQ(stateValues[0].value(), (int32_t)value); + break; + case StateValue::ContentsCase::kGroupId: + EXPECT_EQ(stateValues[0].group_id(), value); + break; + default: + FAIL() << "State value should have either a value or a group id"; + } +} bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { if (s1.field() != s2.field()) { return false; diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 1220019e2353..c51491244fd8 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -347,6 +347,8 @@ void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int atomId, int uid, const std::string& tag); void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag); +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value); struct DimensionsPair { DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2) diff --git a/core/api/current.txt b/core/api/current.txt index e302b25873b4..00acae1b1025 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6968,6 +6968,7 @@ package android.app.admin { method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]); method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int); + method public boolean hasKeyPair(@NonNull String); method public boolean hasLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName); method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]); method public boolean installExistingPackage(@NonNull android.content.ComponentName, String); @@ -7343,6 +7344,7 @@ package android.app.admin { field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a field public static final int TAG_OS_STARTUP = 210009; // 0x33459 + field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473 field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461 field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460 field public static final int TAG_PASSWORD_HISTORY_LENGTH_SET = 210018; // 0x33462 @@ -9096,6 +9098,15 @@ package android.bluetooth { field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR; } + public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile { + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize(); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice); + method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]); + field public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; + } + public final class BluetoothManager { method public android.bluetooth.BluetoothAdapter getAdapter(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); @@ -45126,6 +45137,7 @@ package android.telephony { field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe field public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1; // 0xffffffff field public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool"; + field public static final String EXTRA_REBROADCAST_ON_UNLOCK = "android.telephony.extra.REBROADCAST_ON_UNLOCK"; field public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; field public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; field public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int"; @@ -45153,6 +45165,7 @@ package android.telephony { field public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool"; field public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool"; field public static final String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool"; + field public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = "call_composer_picture_server_url_string"; field public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array"; field public static final String KEY_CALL_REDIRECTION_SERVICE_COMPONENT_NAME_STRING = "call_redirection_service_component_name_string"; field public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL = "carrier_allow_deflect_ims_call_bool"; @@ -45342,6 +45355,7 @@ package android.telephony { field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool"; + field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL = "support_add_conference_participants_bool"; field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool"; @@ -45400,6 +45414,7 @@ package android.telephony { } public static final class CarrierConfigManager.Ims { + field public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = "ims.enable_presence_publish_bool"; field public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = "ims.ims_single_registration_required_bool"; field public static final String KEY_PREFIX = "ims."; field public static final String KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT = "ims.wifi_off_deferring_time_millis_int"; @@ -50849,6 +50864,33 @@ package android.view { method public void onActionViewExpanded(); } + public final class ContentInfo { + method @NonNull public android.content.ClipData getClip(); + method @Nullable public android.os.Bundle getExtras(); + method public int getFlags(); + method @Nullable public android.net.Uri getLinkUri(); + method public int getSource(); + method @NonNull public java.util.Map<java.lang.Boolean,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); + field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 + field public static final int SOURCE_APP = 0; // 0x0 + field public static final int SOURCE_AUTOFILL = 4; // 0x4 + field public static final int SOURCE_CLIPBOARD = 1; // 0x1 + field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 + field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 + field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 + } + + public static final class ContentInfo.Builder { + ctor public ContentInfo.Builder(@NonNull android.view.ContentInfo); + ctor public ContentInfo.Builder(@NonNull android.content.ClipData, int); + method @NonNull public android.view.ContentInfo build(); + method @NonNull public android.view.ContentInfo.Builder setClip(@NonNull android.content.ClipData); + method @NonNull public android.view.ContentInfo.Builder setExtras(@Nullable android.os.Bundle); + method @NonNull public android.view.ContentInfo.Builder setFlags(int); + method @NonNull public android.view.ContentInfo.Builder setLinkUri(@Nullable android.net.Uri); + method @NonNull public android.view.ContentInfo.Builder setSource(int); + } + public interface ContextMenu extends android.view.Menu { method public void clearHeader(); method public android.view.ContextMenu setHeaderIcon(@DrawableRes int); @@ -52064,34 +52106,7 @@ package android.view { } public interface OnReceiveContentListener { - method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.View, @NonNull android.view.OnReceiveContentListener.Payload); - } - - public static final class OnReceiveContentListener.Payload { - method @NonNull public android.content.ClipData getClip(); - method @Nullable public android.os.Bundle getExtras(); - method public int getFlags(); - method @Nullable public android.net.Uri getLinkUri(); - method public int getSource(); - method @NonNull public java.util.Map<java.lang.Boolean,android.view.OnReceiveContentListener.Payload> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>); - field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 - field public static final int SOURCE_APP = 0; // 0x0 - field public static final int SOURCE_AUTOFILL = 4; // 0x4 - field public static final int SOURCE_CLIPBOARD = 1; // 0x1 - field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 - field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 - field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 - } - - public static final class OnReceiveContentListener.Payload.Builder { - ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.view.OnReceiveContentListener.Payload); - ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.content.ClipData, int); - method @NonNull public android.view.OnReceiveContentListener.Payload build(); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setClip(@NonNull android.content.ClipData); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setExtras(@Nullable android.os.Bundle); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setFlags(int); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setLinkUri(@Nullable android.net.Uri); - method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setSource(int); + method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo); } public abstract class OrientationEventListener { @@ -52842,7 +52857,7 @@ package android.view { method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int); method public void onProvideStructure(android.view.ViewStructure); method public void onProvideVirtualStructure(android.view.ViewStructure); - method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload); + method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.ContentInfo); method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int); method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable); method public void onRtlPropertiesChanged(int); @@ -52868,7 +52883,7 @@ package android.view { method public boolean performHapticFeedback(int, int); method public boolean performLongClick(); method public boolean performLongClick(float, float); - method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload); + method @Nullable public android.view.ContentInfo performReceiveContent(@NonNull android.view.ContentInfo); method public void playSoundEffect(int); method public boolean post(Runnable); method public boolean postDelayed(Runnable, long); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 99179ec89cbc..a5f2b6ac3af8 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -248,7 +248,9 @@ package android { field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK"; field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS"; field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS"; + field public static final String WIFI_ACCESS_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS"; field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"; + field public static final String WIFI_UPDATE_COEX_UNSAFE_CHANNELS = "android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS"; field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"; field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG"; field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE"; @@ -6504,6 +6506,7 @@ package android.net { field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int TYPE_PROXY = 16; // 0x10 field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } @@ -8420,6 +8423,8 @@ package android.provider { field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native"; field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot"; field public static final String NAMESPACE_SCHEDULER = "scheduler"; + field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native"; + field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot"; field @Deprecated public static final String NAMESPACE_STORAGE = "storage"; field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; field public static final String NAMESPACE_SYSTEMUI = "systemui"; @@ -10827,6 +10832,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isLteCdmaEvdoGsmWcdmaEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int); method public boolean isNrDualConnectivityEnabled(); @@ -11432,6 +11438,61 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR; } + public interface DelegateMessageCallback { + method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); + method public void onMessageSendFailure(@NonNull String, int); + method public void onMessageSent(@NonNull String); + } + + public final class DelegateRegistrationState implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags(); + method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags(); + method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR; + field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1 + field public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; // 0x2 + field public static final int DEREGISTERED_REASON_UNKNOWN = 0; // 0x0 + field public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; // 0x6 + field public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; // 0x5 + field public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; // 0x3 + field public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; // 0x4 + } + + public static final class DelegateRegistrationState.Builder { + ctor public DelegateRegistrationState.Builder(); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteredFeatureTag(@NonNull String, int); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String); + method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>); + method @NonNull public android.telephony.ims.DelegateRegistrationState build(); + } + + public final class DelegateRequest implements android.os.Parcelable { + ctor public DelegateRequest(@NonNull java.util.Set<java.lang.String>); + method public int describeContents(); + method @NonNull public java.util.Set<java.lang.String> getFeatureTags(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRequest> CREATOR; + } + + public interface DelegateStateCallback { + method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>); + method public void onDestroyed(int); + method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState); + method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + + public final class FeatureTagState implements android.os.Parcelable { + ctor public FeatureTagState(@NonNull String, int); + method public int describeContents(); + method @NonNull public String getFeatureTag(); + method public int getState(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.FeatureTagState> CREATOR; + } + public final class ImsCallForwardInfo implements android.os.Parcelable { ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int); method public int describeContents(); @@ -11468,6 +11529,7 @@ package android.telephony.ims { method public boolean getCallExtraBoolean(String, boolean); method public int getCallExtraInt(String); method public int getCallExtraInt(String, int); + method @Nullable public <T extends android.os.Parcelable> T getCallExtraParcelable(@Nullable String); method public android.os.Bundle getCallExtras(); method public int getCallType(); method public static int getCallTypeFromVideoState(int); @@ -11490,6 +11552,7 @@ package android.telephony.ims { method public void setCallExtra(String, String); method public void setCallExtraBoolean(String, boolean); method public void setCallExtraInt(String, int); + method public void setCallExtraParcelable(@NonNull String, @NonNull android.os.Parcelable); method public void setCallRestrictCause(int); method public void setCallerNumberVerificationStatus(int); method public void setEmergencyCallRouting(int); @@ -11524,6 +11587,7 @@ package android.telephony.ims { field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE"; field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE"; field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech"; + field public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT"; field public static final String EXTRA_CHILD_NUMBER = "ChildNum"; field public static final String EXTRA_CNA = "cna"; field public static final String EXTRA_CNAP = "cnap"; @@ -11533,8 +11597,11 @@ package android.telephony.ims { field public static final String EXTRA_EMERGENCY_CALL = "e_call"; field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; field public static final String EXTRA_IS_CALL_PULL = "CallPull"; + field public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION"; field public static final String EXTRA_OI = "oi"; field public static final String EXTRA_OIR = "oir"; + field public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL"; + field public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY"; field public static final String EXTRA_REMOTE_URI = "remote_uri"; field public static final String EXTRA_USSD = "ussd"; field public static final int OIR_DEFAULT = 0; // 0x0 @@ -11542,6 +11609,8 @@ package android.telephony.ims { field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4 field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1 field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3 + field public static final int PRIORITY_NORMAL = 0; // 0x0 + field public static final int PRIORITY_URGENT = 1; // 0x1 field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2 field public static final int SERVICE_TYPE_NONE = 0; // 0x0 field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1 @@ -11952,8 +12021,107 @@ package android.telephony.ims { field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR; } + public interface SipDelegateConnection { + method public void notifyMessageReceiveError(@NonNull String, int); + method public void notifyMessageReceived(@NonNull String); + method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); + } + + public final class SipDelegateImsConfiguration implements android.os.Parcelable { + method public boolean containsKey(@NonNull String); + method @NonNull public android.os.PersistableBundle copyBundle(); + method public int describeContents(); + method public boolean getBoolean(@NonNull String, boolean); + method public int getInt(@NonNull String, int); + method @Nullable public String getString(@NonNull String); + method public long getVersion(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR; + field public static final String IPTYPE_IPV4 = "IPV4"; + field public static final String IPTYPE_IPV6 = "IPV6"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string"; + field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string"; + field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string"; + field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string"; + field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string"; + field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool"; + field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool"; + field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int"; + field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string"; + field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string"; + field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string"; + field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string"; + field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string"; + field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int"; + field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int"; + field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int"; + field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string"; + field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string"; + field public static final String SIP_TRANSPORT_TCP = "TCP"; + field public static final String SIP_TRANSPORT_UDP = "UDP"; + } + + public static final class SipDelegateImsConfiguration.Builder { + ctor public SipDelegateImsConfiguration.Builder(int); + ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String); + method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build(); + } + public class SipDelegateManager { + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException; + field public static final int DENIED_REASON_INVALID = 4; // 0x4 + field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1 + field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2 + field public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; // 0x3 + field public static final int DENIED_REASON_UNKNOWN = 0; // 0x0 + field public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; // 0x2 + field public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; // 0x1 + field public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; // 0xb + field public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; // 0x5 + field public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; // 0x6 + field public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; // 0x4 + field public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; // 0x3 + field public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; // 0x8 + field public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; // 0x9 + field public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; // 0xa + field public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; // 0x7 + field public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; // 0x0 + field public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; // 0x2 + field public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; // 0x1 + field public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; // 0x4 + field public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; // 0x0 + field public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; // 0x3 + } + + public final class SipMessage implements android.os.Parcelable { + ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]); + method public int describeContents(); + method @NonNull public byte[] getContent(); + method @NonNull public String getHeaderSection(); + method @NonNull public String getStartLine(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR; } } @@ -12047,6 +12215,19 @@ package android.telephony.ims.feature { package android.telephony.ims.stub { + public interface DelegateConnectionMessageCallback { + method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage); + method public void onMessageSendFailure(@NonNull String, int); + method public void onMessageSent(@NonNull String); + } + + public interface DelegateConnectionStateCallback { + method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection); + method public void onDestroyed(int); + method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>); + method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration); + } + public class ImsCallSessionImplBase implements java.lang.AutoCloseable { ctor public ImsCallSessionImplBase(); method public void accept(int, android.telephony.ims.ImsStreamMediaProfile); @@ -12207,8 +12388,17 @@ package android.telephony.ims.stub { method public int updateColr(int); } + public interface SipDelegate { + method public void closeDialog(@NonNull String); + method public void notifyMessageReceiveError(@NonNull String, int); + method public void notifyMessageReceived(@NonNull String); + method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long); + } + public class SipTransportImplBase { ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor); + method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback); + method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int); } } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5434bcca0575..e392ed7b6e2d 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -7,6 +7,7 @@ package android { field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE"; field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE"; + field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"; field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE"; field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA"; field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"; @@ -1499,7 +1500,6 @@ package android.provider { field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; field public static final String HIDDEN_API_POLICY = "hidden_api_policy"; field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs"; - field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch"; field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; field public static final String LOW_POWER_MODE = "low_power"; field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java index fc93f03d76cf..08861d42be39 100644 --- a/core/java/android/annotation/RequiresFeature.java +++ b/core/java/android/annotation/RequiresFeature.java @@ -30,7 +30,6 @@ import java.lang.annotation.Target; * Denotes that the annotated element requires one or more device features. This * is used to auto-generate documentation. * - * @see PackageManager#hasSystemFeature(String) * @hide */ @Retention(SOURCE) @@ -38,8 +37,16 @@ import java.lang.annotation.Target; public @interface RequiresFeature { /** * The name of the device feature that is required. - * - * @see PackageManager#hasSystemFeature(String) */ String value(); + + /** + * Defines the name of the method that should be called to check whether the feature is + * available, using the same signature format as javadoc. The feature checking method can have + * multiple parameters, but the feature name parameter must be of type String and must also be + * the first String-type parameter. + * <p> + * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}. + */ + String enforcement() default("android.content.pm.PackageManager#hasSystemFeature"); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index fc95718cf0e4..5ee5597e1984 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2590,7 +2590,7 @@ public class Activity extends ContextThemeWrapper protected void onStop() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); - mActivityTransitionState.onStop(); + mActivityTransitionState.onStop(this); dispatchActivityStopped(); mTranslucentCallback = null; mCalled = true; @@ -5189,8 +5189,8 @@ public class Activity extends ContextThemeWrapper * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}. * </p> * <p> - * The <a href="https://github.com/googlesamples/android-RuntimePermissions"> - * RuntimePermissions</a> sample app demonstrates how to use this method to + * The <a href="https://github.com/android/permissions-samples"> + * RuntimePermissions</a> sample apps demonstrate how to use this method to * request permissions at run time. * </p> * diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5537edb8b957..e3048dff1bda 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1854,16 +1854,12 @@ public class ActivityManager { * the recent tasks. */ @Deprecated - public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) - throws SecurityException { - try { - if (maxNum < 0) { - throw new IllegalArgumentException("The requested number of tasks should be >= 0"); - } - return getTaskService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws SecurityException { + if (maxNum < 0) { + throw new IllegalArgumentException("The requested number of tasks should be >= 0"); } + return ActivityTaskManager.getInstance().getRecentTasks( + maxNum, flags, mContext.getUserId()); } /** @@ -2084,11 +2080,7 @@ public class ActivityManager { @Deprecated public List<RunningTaskInfo> getRunningTasks(int maxNum) throws SecurityException { - try { - return getTaskService().getTasks(maxNum); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return ActivityTaskManager.getInstance().getTasks(maxNum); } /** diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index ef146b118c33..0b0781e917e3 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -27,6 +27,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.TestApi; +import android.app.ExitTransitionCoordinator.ActivityExitTransitionCallbacks; +import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -44,8 +46,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; -import android.transition.Transition; -import android.transition.TransitionListenerAdapter; import android.transition.TransitionManager; import android.util.Pair; import android.util.Slog; @@ -806,8 +806,11 @@ public class ActivityOptions { public static ActivityOptions makeSceneTransitionAnimation(Activity activity, Pair<View, String>... sharedElements) { ActivityOptions opts = new ActivityOptions(); - makeSceneTransitionAnimation(activity, activity.getWindow(), opts, - activity.mExitTransitionListener, sharedElements); + ExitTransitionCoordinator exit = makeSceneTransitionAnimation( + new ActivityExitTransitionCallbacks(activity), activity.mExitTransitionListener, + activity.getWindow(), opts, sharedElements); + opts.mExitCoordinatorIndex = + activity.mActivityTransitionState.addExitTransitionCoordinator(exit); return opts; } @@ -823,25 +826,19 @@ public class ActivityOptions { * @hide */ @SafeVarargs - public static ActivityOptions startSharedElementAnimation(Window window, + public static Pair<ActivityOptions, ExitTransitionCoordinator> startSharedElementAnimation( + Window window, ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback, Pair<View, String>... sharedElements) { ActivityOptions opts = new ActivityOptions(); - final View decorView = window.getDecorView(); - if (decorView == null) { - return opts; - } - final ExitTransitionCoordinator exit = - makeSceneTransitionAnimation(null, window, opts, null, sharedElements); - if (exit != null) { - HideWindowListener listener = new HideWindowListener(window, exit); - exit.setHideSharedElementsCallback(listener); - exit.startExit(); - } - return opts; + ExitTransitionCoordinator exit = makeSceneTransitionAnimation( + exitCallbacks, callback, window, opts, sharedElements); + opts.mExitCoordinatorIndex = -1; + return Pair.create(opts, exit); } /** - * This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])} + * This method should be called when the + * {@link #startSharedElementAnimation(Window, ExitTransitionCallbacks, Pair[])} * animation must be stopped and the Views reset. This can happen if there was an error * from startActivity or a springboard activity and the animation should stop and reset. * @@ -864,9 +861,9 @@ public class ActivityOptions { } } - static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window, - ActivityOptions opts, SharedElementCallback callback, - Pair<View, String>[] sharedElements) { + static ExitTransitionCoordinator makeSceneTransitionAnimation( + ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback, Window window, + ActivityOptions opts, Pair<View, String>[] sharedElements) { if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) { opts.mAnimationType = ANIM_DEFAULT; return null; @@ -892,17 +889,11 @@ public class ActivityOptions { } } - ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window, + ExitTransitionCoordinator exit = new ExitTransitionCoordinator(exitCallbacks, window, callback, names, names, views, false); opts.mTransitionReceiver = exit; opts.mSharedElementNames = names; - opts.mIsReturning = (activity == null); - if (activity == null) { - opts.mExitCoordinatorIndex = -1; - } else { - opts.mExitCoordinatorIndex = - activity.mActivityTransitionState.addExitTransitionCoordinator(exit); - } + opts.mIsReturning = false; return exit; } @@ -928,8 +919,12 @@ public class ActivityOptions { opts.mIsReturning = true; opts.mResultCode = resultCode; opts.mResultData = resultData; - opts.mExitCoordinatorIndex = - activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator); + if (activity == null) { + opts.mExitCoordinatorIndex = -1; + } else { + opts.mExitCoordinatorIndex = + activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator); + } return opts; } @@ -1868,67 +1863,6 @@ public class ActivityOptions { + mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight; } - private static class HideWindowListener extends TransitionListenerAdapter - implements ExitTransitionCoordinator.HideSharedElementsCallback { - private final Window mWindow; - private final ExitTransitionCoordinator mExit; - private final boolean mWaitingForTransition; - private boolean mTransitionEnded; - private boolean mSharedElementHidden; - private ArrayList<View> mSharedElements; - - public HideWindowListener(Window window, ExitTransitionCoordinator exit) { - mWindow = window; - mExit = exit; - mSharedElements = new ArrayList<>(exit.mSharedElements); - Transition transition = mWindow.getExitTransition(); - if (transition != null) { - transition.addListener(this); - mWaitingForTransition = true; - } else { - mWaitingForTransition = false; - } - View decorView = mWindow.getDecorView(); - if (decorView != null) { - if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) { - throw new IllegalStateException( - "Cannot start a transition while one is running"); - } - decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit); - } - } - - @Override - public void onTransitionEnd(Transition transition) { - mTransitionEnded = true; - hideWhenDone(); - transition.removeListener(this); - } - - @Override - public void hideSharedElements() { - mSharedElementHidden = true; - hideWhenDone(); - } - - private void hideWhenDone() { - if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) { - mExit.resetViews(); - int numSharedElements = mSharedElements.size(); - for (int i = 0; i < numSharedElements; i++) { - View view = mSharedElements.get(i); - view.requestLayout(); - } - View decorView = mWindow.getDecorView(); - if (decorView != null) { - decorView.setTagInternal( - com.android.internal.R.id.cross_task_transition, null); - decorView.setVisibility(View.GONE); - } - } - } - } - /** * The information about the source of activity launch. E.g. describe an activity is launched * from launcher by receiving a motion event with a timestamp. diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java index c7b90897c8e7..03c1a011f198 100644 --- a/core/java/android/app/ActivityTaskManager.java +++ b/core/java/android/app/ActivityTaskManager.java @@ -27,7 +27,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; -import android.os.Handler; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -35,6 +34,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.util.DisplayMetrics; import android.util.Singleton; +import android.view.RemoteAnimationDefinition; import java.util.List; @@ -147,7 +147,20 @@ public class ActivityTaskManager { private static int sMaxRecentTasks = -1; - ActivityTaskManager(Context context, Handler handler) { + private static final Singleton<ActivityTaskManager> sInstance = + new Singleton<ActivityTaskManager>() { + @Override + protected ActivityTaskManager create() { + return new ActivityTaskManager(); + } + }; + + private ActivityTaskManager() { + } + + /** @hide */ + public static ActivityTaskManager getInstance() { + return sInstance.get(); } /** @hide */ @@ -444,6 +457,98 @@ public class ActivityTaskManager { } /** + * @return List of running tasks. + * @hide + */ + public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { + return getTasks(maxNum, false /* filterForVisibleRecents */); + } + + /** + * @return List of running tasks that can be filtered by visibility in recents. + * @hide + */ + public List<ActivityManager.RunningTaskInfo> getTasks( + int maxNum, boolean filterOnlyVisibleRecents) { + try { + return getService().getTasks(maxNum, filterOnlyVisibleRecents); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @return List of recent tasks. + * @hide + */ + public List<ActivityManager.RecentTaskInfo> getRecentTasks( + int maxNum, int flags, int userId) { + try { + return getService().getRecentTasks(maxNum, flags, userId).getList(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void registerTaskStackListener(TaskStackListener listener) { + try { + getService().registerTaskStackListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public void unregisterTaskStackListener(TaskStackListener listener) { + try { + getService().unregisterTaskStackListener(listener); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public Rect getTaskBounds(int taskId) { + try { + return getService().getTaskBounds(taskId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers remote animations for a display. + * @hide + */ + public void registerRemoteAnimationsForDisplay( + int displayId, RemoteAnimationDefinition definition) { + try { + getService().registerRemoteAnimationsForDisplay(displayId, definition); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public boolean isInLockTaskMode() { + try { + return getService().isInLockTaskMode(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + public boolean removeTask(int taskId) { + try { + return getService().removeTask(taskId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Information you can retrieve about a root task in the system. * @hide */ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2211807a422b..d9c0c71d44ae 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -85,6 +85,7 @@ import android.database.sqlite.SQLiteDebug.DbStats; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.HardwareRenderer; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerGlobal; import android.inputmethodservice.InputMethodService; import android.media.MediaFrameworkInitializer; @@ -117,6 +118,7 @@ import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SharedMemory; import android.os.StatsFrameworkInitializer; import android.os.StatsServiceManager; import android.os.StrictMode; @@ -844,6 +846,8 @@ public final class ActivityThread extends ClientTransactionHandler { long[] disabledCompatChanges; + SharedMemory mSerializedSystemFontMap; + @Override public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; @@ -1054,7 +1058,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, - ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) { + ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges, + SharedMemory serializedSystemFontMap) { if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1103,6 +1108,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.autofillOptions = autofillOptions; data.contentCaptureOptions = contentCaptureOptions; data.disabledCompatChanges = disabledCompatChanges; + data.mSerializedSystemFontMap = serializedSystemFontMap; sendMessage(H.BIND_APPLICATION, data); } @@ -6411,6 +6417,13 @@ public final class ActivityThread extends ClientTransactionHandler { */ LocaleList.setDefault(data.config.getLocales()); + try { + Typeface.setSystemFontMap(data.mSerializedSystemFontMap); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to parse serialized system font map"); + Typeface.loadPreinstalledSystemFontMap(); + } + synchronized (mResourcesManager) { /* * Update the system configuration since its preloaded and might not diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 5c4125e01dfd..62619509184a 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -247,14 +247,14 @@ class ActivityTransitionState { mEnterActivityOptions = null; } - public void onStop() { + public void onStop(Activity activity) { restoreExitedViews(); if (mEnterTransitionCoordinator != null) { mEnterTransitionCoordinator.stop(); mEnterTransitionCoordinator = null; } if (mReturnExitCoordinator != null) { - mReturnExitCoordinator.stop(); + mReturnExitCoordinator.stop(activity); mReturnExitCoordinator = null; } } @@ -331,7 +331,8 @@ class ActivityTransitionState { } } - mReturnExitCoordinator = new ExitTransitionCoordinator(activity, + mReturnExitCoordinator = new ExitTransitionCoordinator( + new ExitTransitionCoordinator.ActivityExitTransitionCallbacks(activity), activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames, null, null, true); if (enterViewsTransition != null && decor != null) { @@ -341,12 +342,11 @@ class ActivityTransitionState { final ViewGroup finalDecor = decor; OneShotPreDrawListener.add(decor, () -> { if (mReturnExitCoordinator != null) { - mReturnExitCoordinator.startExit(activity.mResultCode, - activity.mResultData); + mReturnExitCoordinator.startExit(activity); } }); } else { - mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData); + mReturnExitCoordinator.startExit(activity); } } return true; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index cdfe41e85917..4dd6a7efe7c5 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -358,7 +358,7 @@ public class AppOpsManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, prefix = { "MODE_" }, value = { + @IntDef(prefix = { "MODE_" }, value = { MODE_ALLOWED, MODE_IGNORED, MODE_ERRORED, diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index 68824cd26eaa..9fdff5979cd0 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -18,6 +18,7 @@ package android.app; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.app.SharedElementCallback.OnSharedElementsReadyListener; import android.content.Intent; import android.graphics.Color; @@ -45,15 +46,17 @@ import java.util.ArrayList; * This ActivityTransitionCoordinator is created in ActivityOptions#makeSceneTransitionAnimation * to govern the exit of the Scene and the shared elements when calling an Activity as well as * the reentry of the Scene when coming back from the called Activity. + * + * @hide */ -class ExitTransitionCoordinator extends ActivityTransitionCoordinator { +public class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private static final String TAG = "ExitTransitionCoordinator"; static long sMaxWaitMillis = 1000; private Bundle mSharedElementBundle; private boolean mExitNotified; private boolean mSharedElementNotified; - private Activity mActivity; + private ExitTransitionCallbacks mExitCallbacks; private boolean mIsBackgroundReady; private boolean mIsCanceled; private Handler mHandler; @@ -62,20 +65,15 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private Bundle mExitSharedElementBundle; private boolean mIsExitStarted; private boolean mSharedElementsHidden; - private HideSharedElementsCallback mHideSharedElementsCallback; - public ExitTransitionCoordinator(Activity activity, Window window, - SharedElementCallback listener, ArrayList<String> names, + public ExitTransitionCoordinator(ExitTransitionCallbacks exitCallbacks, + Window window, SharedElementCallback listener, ArrayList<String> names, ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) { super(window, names, listener, isReturning); viewsReady(mapSharedElements(accepted, mapped)); stripOffscreenViews(); mIsBackgroundReady = !isReturning; - mActivity = activity; - } - - void setHideSharedElementsCallback(HideSharedElementsCallback callback) { - mHideSharedElementsCallback = callback; + mExitCallbacks = exitCallbacks; } @Override @@ -190,8 +188,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { private void hideSharedElements() { moveSharedElementsFromOverlay(); - if (mHideSharedElementsCallback != null) { - mHideSharedElementsCallback.hideSharedElements(); + if (mExitCallbacks != null) { + mExitCallbacks.hideSharedElements(); } if (!mIsHidden) { hideViews(mSharedElements); @@ -210,20 +208,16 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { decorView.suppressLayout(true); } moveSharedElementsToOverlay(); - startTransition(new Runnable() { - @Override - public void run() { - if (mActivity != null) { - beginTransitions(); - } else { - startExitTransition(); - } - } - }); + startTransition(this::beginTransitions); } } - public void startExit(int resultCode, Intent data) { + /** + * Starts the exit animation and sends back the activity result + */ + public void startExit(Activity activity) { + int resultCode = activity.mResultCode; + Intent data = activity.mResultData; if (!mIsExitStarted) { mIsExitStarted = true; pauseInput(); @@ -247,9 +241,9 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { .getApplicationInfo().targetSdkVersion >= VERSION_CODES.M; ArrayList<String> sharedElementNames = targetsM ? mSharedElementNames : mAllSharedElementNames; - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this, + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, this, sharedElementNames, resultCode, data); - mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() { + activity.convertToTranslucent(new Activity.TranslucentConversionListener() { @Override public void onTranslucentConversionComplete(boolean drawComplete) { if (!mIsCanceled) { @@ -257,21 +251,19 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } }, options); - startTransition(new Runnable() { - @Override - public void run() { - startExitTransition(); - } - }); + startTransition(this::startExitTransition); } } - public void stop() { - if (mIsReturning && mActivity != null) { + /** + * Called from {@link Activity#onStop()} + */ + public void stop(Activity activity) { + if (mIsReturning && mExitCallbacks != null) { // Override the previous ActivityOptions. We don't want the // activity to have options since we're essentially canceling the // transition and finishing right now. - mActivity.convertToTranslucent(null, null); + activity.convertToTranslucent(null, null); finish(); } } @@ -434,7 +426,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { mSharedElementNotified = true; delayCancel(); - if (!mActivity.isTopOfTask()) { + if (mExitCallbacks.isReturnTransitionAllowed()) { mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null); } @@ -474,22 +466,20 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } private void finishIfNecessary() { - if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() || - mSharedElementsHidden)) { + if (mIsReturning && mExitNotified && mExitCallbacks != null && (mSharedElements.isEmpty() + || mSharedElementsHidden)) { finish(); } if (!mIsReturning && mExitNotified) { - mActivity = null; // don't need it anymore + mExitCallbacks = null; // don't need it anymore } } private void finish() { stopCancel(); - if (mActivity != null) { - mActivity.mActivityTransitionState.clear(); - mActivity.finish(); - mActivity.overridePendingTransition(0, 0); - mActivity = null; + if (mExitCallbacks != null) { + mExitCallbacks.onFinish(); + mExitCallbacks = null; } // Clear the state so that we can't hold any references accidentally and leak memory. clearState(); @@ -529,7 +519,49 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { } } - interface HideSharedElementsCallback { - void hideSharedElements(); + /** + * @hide + */ + public interface ExitTransitionCallbacks { + + /** + * Returns true if reverse exit animation is supported + */ + boolean isReturnTransitionAllowed(); + + /** + * Called then the transition finishes + */ + void onFinish(); + + /** + * Optional callback when the transition is hiding elements in the source surface + */ + default void hideSharedElements() { }; + } + + /** + * @hide + */ + public static class ActivityExitTransitionCallbacks implements ExitTransitionCallbacks { + + @NonNull + final Activity mActivity; + + ActivityExitTransitionCallbacks(@NonNull Activity activity) { + mActivity = activity; + } + + @Override + public boolean isReturnTransitionAllowed() { + return !mActivity.isTopOfTask(); + } + + @Override + public void onFinish() { + mActivity.mActivityTransitionState.clear(); + mActivity.finish(); + mActivity.overridePendingTransition(0, 0); + } } } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index bd5913efdecb..ab48baea48e8 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -83,6 +83,15 @@ import java.util.List; * * {@hide} */ +// TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external +// caller go through that call instead. This would help us better separate and control the API +// surface exposed. +// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token') +// to a separate interface that is only available to the Activity. +// TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce +// interface duplication. +// TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be +// going through task organizer now. interface IActivityTaskManager { int startActivity(in IApplicationThread caller, in String callingPackage, in String callingFeatureId, in Intent intent, in String resolvedType, @@ -154,9 +163,7 @@ interface IActivityTaskManager { void setFocusedTask(int taskId); boolean removeTask(int taskId); void removeAllVisibleRecentTasks(); - List<ActivityManager.RunningTaskInfo> getTasks(int maxNum); - List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, - boolean filterOnlyVisibleRecents); + List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents); boolean shouldUpRecreateTask(in IBinder token, in String destAffinity); boolean navigateUpTo(in IBinder token, in Intent target, int resultCode, in Intent resultData); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 22ca42eb9cf4..890e957bdff4 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -43,6 +43,7 @@ import android.os.IInterface; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.RemoteCallback; +import android.os.SharedMemory; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; @@ -75,7 +76,8 @@ oneway interface IApplicationThread { boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, - in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges); + in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges, + in SharedMemory serializedSystemFontMap); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); void scheduleServiceArgs(IBinder token, in ParceledListSlice args); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a886beddf64c..a1135809fd4c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -30,6 +30,7 @@ import android.annotation.IdRes; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.Px; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -4873,6 +4874,7 @@ public class Notification implements Parcelable // Small icon doesn't need to be reset, as it's always set. Resetting would prevent // re-using the drawable when the notification is updated. contentView.setBoolean(R.id.expand_button, "setExpanded", false); + contentView.setViewVisibility(R.id.app_name_text, View.GONE); contentView.setTextViewText(R.id.app_name_text, null); contentView.setViewVisibility(R.id.chronometer, View.GONE); contentView.setViewVisibility(R.id.header_text, View.GONE); @@ -5105,33 +5107,29 @@ public class Notification implements Parcelable if (result == null) { result = new TemplateBindResult(); } - boolean largeIconShown = bindLargeIcon(contentView, p); + final boolean largeIconShown = bindLargeIcon(contentView, p); calculateLargeIconMarginEnd(largeIconShown, result); if (p.mHeaderless) { // views in the headerless (collapsed) state - contentView.setViewLayoutMarginEnd(R.id.notification_standard_view_column, - result.getHeadingExtraMarginEnd()); + result.mHeadingExtraMarginSet.applyToView(contentView, + R.id.notification_headerless_view_column); } else { // views in states with a header (big states) - contentView.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", - result.getHeadingExtraMarginEnd()); - contentView.setViewLayoutMarginEnd(R.id.line1, result.getTitleMarginEnd()); + result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header); + result.mTitleMarginSet.applyToView(contentView, R.id.line1); } } private void calculateLargeIconMarginEnd(boolean largeIconShown, @NonNull TemplateBindResult result) { - int contentMargin = mContext.getResources().getDimensionPixelSize( + final Resources resources = mContext.getResources(); + final int contentMargin = resources.getDimensionPixelOffset( R.dimen.notification_content_margin_end); - int expanderSize = mContext.getResources().getDimensionPixelSize( + final int expanderSize = resources.getDimensionPixelSize( R.dimen.notification_header_expand_icon_size) - contentMargin; - int extraMarginEnd = 0; - if (largeIconShown) { - int iconSize = mContext.getResources().getDimensionPixelSize( - R.dimen.notification_right_icon_size); - extraMarginEnd = iconSize + contentMargin; - } - result.setRightIconState(largeIconShown, extraMarginEnd, expanderSize); + final int extraMarginEndIfVisible = resources.getDimensionPixelSize( + R.dimen.notification_right_icon_size) + contentMargin; + result.setRightIconState(largeIconShown, extraMarginEndIfVisible, expanderSize); } /** @@ -5153,9 +5151,14 @@ public class Notification implements Parcelable private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) { bindSmallIcon(contentView, p); - boolean hasTextToLeft = bindHeaderAppName(contentView, p); + // Populate text left-to-right so that separators are only shown between strings + boolean hasTextToLeft = bindHeaderAppName(contentView, p, false /* force */); hasTextToLeft |= bindHeaderTextSecondary(contentView, p, hasTextToLeft); hasTextToLeft |= bindHeaderText(contentView, p, hasTextToLeft); + if (!hasTextToLeft) { + // If there's still no text, force add the app name so there is some text. + hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */); + } bindHeaderChronometerAndTime(contentView, p, hasTextToLeft); bindProfileBadge(contentView, p); bindAlertedIcon(contentView, p); @@ -5219,7 +5222,7 @@ public class Notification implements Parcelable && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) { summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT); } - if (summaryText != null) { + if (!TextUtils.isEmpty(summaryText)) { // TODO: Remove the span entirely to only have the string with propper formating. contentView.setTextViewText(R.id.header_text, processTextSpans( processLegacyText(summaryText))); @@ -5291,13 +5294,13 @@ public class Notification implements Parcelable /** * @return true if the app name will be visible */ - private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) { - if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED) { - contentView.setViewVisibility(R.id.app_name_text, View.GONE); + private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p, + boolean force) { + if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED && !force) { + // unless the force flag is set, don't show the app name in the minimized state. return false; } if (p.mHeaderless && p.hasTitle()) { - contentView.setViewVisibility(R.id.app_name_text, View.GONE); // the headerless template will have the TITLE in this position; return true to // keep the divider visible between that title and the next text element. return true; @@ -5929,8 +5932,7 @@ public class Notification implements Parcelable } int color; - int background = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + int background = obtainBackgroundColor(); if (rawColor == COLOR_DEFAULT) { ensureColors(p); color = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); @@ -5963,8 +5965,7 @@ public class Notification implements Parcelable if (mNeutralColor != COLOR_INVALID) { return mNeutralColor; } - int background = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + int background = obtainBackgroundColor(); mNeutralColor = ContrastColorUtil.resolveDefaultColor(mContext, background, mInNightMode); if (Color.alpha(mNeutralColor) < 255) { @@ -6115,6 +6116,21 @@ public class Notification implements Parcelable return mN; } + private @ColorInt int obtainBackgroundColor() { + int defaultColor = mInNightMode ? Color.BLACK : Color.WHITE; + Resources.Theme theme = mContext.getTheme(); + if (theme == null) { + return defaultColor; + } + TypedArray ta = theme.obtainStyledAttributes(new int[]{R.attr.colorBackground}); + if (ta == null) { + return defaultColor; + } + int background = ta.getColor(0, defaultColor); + ta.recycle(); + return background; + } + /** * Apply this Builder to an existing {@link Notification} object. * @@ -6248,8 +6264,7 @@ public class Notification implements Parcelable private int resolveBackgroundColor(StandardTemplateParams p) { int backgroundColor = getBackgroundColor(p); if (backgroundColor == COLOR_DEFAULT) { - backgroundColor = mContext.getColor( - com.android.internal.R.color.notification_material_background_color); + backgroundColor = obtainBackgroundColor(); } return backgroundColor; } @@ -7759,8 +7774,10 @@ public class Notification implements Parcelable addExtras(mBuilder.mN.extras); if (!isConversationLayout) { // also update the end margin if there is an image + // NOTE: This template doesn't support moving this icon to the left, so we don't + // need to fully apply the MarginSet contentView.setViewLayoutMarginEnd(R.id.notification_messaging, - bindResult.getHeadingExtraMarginEnd()); + bindResult.mHeadingExtraMarginSet.getValue()); } contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor", mBuilder.isColorized(p) @@ -8757,9 +8774,8 @@ public class Notification implements Parcelable if (!headerless) { // also update the end margin to account for the large icon or expander Resources resources = mBuilder.mContext.getResources(); - int endMargin = resources.getDimensionPixelSize( - R.dimen.notification_content_margin_end) + result.getTitleMarginEnd(); - remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin); + result.mTitleMarginSet.applyToView(remoteViews, R.id.notification_main_column, + resources.getDimensionPixelOffset(R.dimen.notification_content_margin_end)); } } @@ -10997,42 +11013,74 @@ public class Notification implements Parcelable */ private static class TemplateBindResult { boolean mRightIconVisible; - int mRightIconMarginEnd; - int mExpanderSize; /** - * @return the margin end that needs to be added to the heading so that it won't overlap + * The margin end that needs to be added to the heading so that it won't overlap * with the large icon. This value includes the space required to accommodate the large * icon, but should be added to the space needed to accommodate the expander. This does * not include the 16dp content margin that all notification views must have. */ - public int getHeadingExtraMarginEnd() { - return mRightIconMarginEnd; - } + public final MarginSet mHeadingExtraMarginSet = new MarginSet(); /** - * @return the margin end that needs to be added to the heading so that it won't overlap + * The margin end that needs to be added to the heading so that it won't overlap * with the large icon. This value includes the space required to accommodate the large * icon as well as the expander. This does not include the 16dp content margin that all * notification views must have. */ - public int getHeadingFullMarginEnd() { - return mRightIconMarginEnd + mExpanderSize; - } + public final MarginSet mHeadingFullMarginSet = new MarginSet(); /** - * @return the margin end that needs to be added to the title text of the big state + * The margin end that needs to be added to the title text of the big state * so that it won't overlap with the large icon, but assuming the text can run under * the expander when that icon is not visible. */ - public int getTitleMarginEnd() { - return mRightIconVisible ? getHeadingFullMarginEnd() : 0; - } + public final MarginSet mTitleMarginSet = new MarginSet(); - public void setRightIconState(boolean visible, int marginEnd, int expanderSize) { + public void setRightIconState(boolean visible, int marginEndIfVisible, int expanderSize) { mRightIconVisible = visible; - mRightIconMarginEnd = marginEnd; - mExpanderSize = expanderSize; + mHeadingExtraMarginSet.setValues(0, marginEndIfVisible); + mHeadingFullMarginSet.setValues(expanderSize, marginEndIfVisible + expanderSize); + mTitleMarginSet.setValues(0, marginEndIfVisible + expanderSize); + } + + /** + * This contains the end margins for a view when the right icon is visible or not. These + * values are both needed so that NotificationGroupingUtil can 'move' the right_icon to the + * left_icon and adjust the margins, and to undo that change as well. + */ + private class MarginSet { + private int mValueIfGone; + private int mValueIfVisible; + + public void setValues(int valueIfGone, int valueIfVisible) { + mValueIfGone = valueIfGone; + mValueIfVisible = valueIfVisible; + } + + public void applyToView(@NonNull RemoteViews views, @IdRes int viewId) { + applyToView(views, viewId, 0); + } + + public void applyToView(@NonNull RemoteViews views, @IdRes int viewId, + @Px int extraMargin) { + final int marginEnd = getValue() + extraMargin; + if (viewId == R.id.notification_header) { + views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd); + } else { + views.setViewLayoutMarginEnd(viewId, marginEnd); + } + if (mRightIconVisible) { + views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible, + mValueIfVisible + extraMargin); + views.setIntTag(viewId, R.id.tag_margin_end_when_icon_gone, + mValueIfGone + extraMargin); + } + } + + public int getValue() { + return mRightIconVisible ? mValueIfVisible : mValueIfGone; + } } } @@ -11074,7 +11122,9 @@ public class Notification implements Parcelable } final boolean hasTitle() { - return title != null && title.length() != 0 && !mHasCustomContent; + // We hide the title when the notification is a decorated custom view so that decorated + // custom views always have to include their own title. + return !TextUtils.isEmpty(title) && !mHasCustomContent; } final StandardTemplateParams viewType(int viewType) { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 73777909d417..7287acdd0241 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -284,8 +284,7 @@ public final class SystemServiceRegistry { new CachedServiceFetcher<ActivityTaskManager>() { @Override public ActivityTaskManager createService(ContextImpl ctx) { - return new ActivityTaskManager( - ctx.getOuterContext(), ctx.mMainThread.getHandler()); + return ActivityTaskManager.getInstance(); }}); registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class, diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING index d2be8a4a6597..36241a8ebe08 100644 --- a/core/java/android/app/TEST_MAPPING +++ b/core/java/android/app/TEST_MAPPING @@ -92,6 +92,54 @@ } ], "file_patterns": ["(/|^)Activity.java"] + }, + { + "name": "CtsContentTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.wm.cts" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.ContextTest" + } + ], + "file_patterns": ["(/|^)ContextImpl.java"] } ], "postsubmit": [ diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index ca67dba45dd0..1a8a4b7f16da 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -214,7 +214,6 @@ public class TaskInfo { */ public int parentTaskId; - /** * Parent bounds. * @hide @@ -227,6 +226,12 @@ public class TaskInfo { */ public boolean isFocused; + /** + * Whether this task is visible. + * @hide + */ + public boolean isVisible; + TaskInfo() { // Do nothing } @@ -311,7 +316,8 @@ public class TaskInfo { && pictureInPictureParams == that.pictureInPictureParams && getWindowingMode() == that.getWindowingMode() && Objects.equals(taskDescription, that.taskDescription) - && isFocused == that.isFocused; + && isFocused == that.isFocused + && isVisible == that.isVisible; } private boolean equalsLetterboxParams(TaskInfo that) { @@ -358,6 +364,7 @@ public class TaskInfo { parentTaskId = source.readInt(); parentBounds = source.readTypedObject(Rect.CREATOR); isFocused = source.readBoolean(); + isVisible = source.readBoolean(); } /** @@ -394,6 +401,7 @@ public class TaskInfo { dest.writeInt(parentTaskId); dest.writeTypedObject(parentBounds, flags); dest.writeBoolean(isFocused); + dest.writeBoolean(isVisible); } @Override @@ -413,12 +421,13 @@ public class TaskInfo { + " topActivityType=" + topActivityType + " pictureInPictureParams=" + pictureInPictureParams + " topActivityInfo=" + topActivityInfo - + " launchCookies" + launchCookies + + " launchCookies=" + launchCookies + " letterboxActivityBounds=" + letterboxActivityBounds + " positionInParent=" + positionInParent + " parentTaskId=" + parentTaskId + " parentBounds=" + parentBounds + " isFocused=" + isFocused + + " isVisible=" + isVisible + "}"; } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 42427fa825eb..5eb1922a163c 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -87,6 +87,7 @@ import android.service.restrictions.RestrictionsReceiver; import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.util.ArraySet; +import android.util.DebugUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -2453,19 +2454,45 @@ public class DevicePolicyManager { @Retention(RetentionPolicy.SOURCE) public @interface PersonalAppsSuspensionReason {} + // TODO(b/172376923) - make all (or none) @TestApi + /** @hide */ @TestApi public static final int OPERATION_LOCK_NOW = 1; + /** @hide */ + public static final int OPERATION_SWITCH_USER = 2; + /** @hide */ + public static final int OPERATION_START_USER_IN_BACKGROUND = 3; + /** @hide */ + public static final int OPERATION_STOP_USER = 4; + /** @hide */ + public static final int OPERATION_CREATE_AND_MANAGE_USER = 5; + /** @hide */ + public static final int OPERATION_REMOVE_USER = 6; + + private static final String PREFIX_OPERATION = "OPERATION_"; + + // TODO(b/172376923) - add all operations /** @hide */ - @IntDef(prefix = "OPERATION_", value = { + @IntDef(prefix = PREFIX_OPERATION, value = { OPERATION_LOCK_NOW, + OPERATION_SWITCH_USER, + OPERATION_START_USER_IN_BACKGROUND, + OPERATION_STOP_USER, + OPERATION_CREATE_AND_MANAGE_USER, + OPERATION_REMOVE_USER }) @Retention(RetentionPolicy.SOURCE) public static @interface DevicePolicyOperation { } + /** @hide */ + public static String operationToString(@DevicePolicyOperation int operation) { + return DebugUtils.constantToString(DevicePolicyManager.class, PREFIX_OPERATION, operation); + } + /** * Return true if the given administrator component is currently active (enabled) in the system. * @@ -3721,6 +3748,27 @@ public class DevicePolicyManager { } /** + * Returns the password complexity that applies to this user, aggregated from other users if + * necessary (for example, if the DPC has set password complexity requirements on the parent + * profile DPM instance of a managed profile user, they would apply to the primary user on the + * device). + * @hide + */ + @PasswordComplexity + public int getAggregatedPasswordComplexityForUser(int userId) { + if (mService == null) { + return PASSWORD_COMPLEXITY_NONE; + } + + try { + return mService.getAggregatedPasswordComplexityForUser(userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + + /** * When called by a profile owner of a managed profile returns true if the profile uses unified * challenge with its parent user. * @@ -5192,9 +5240,22 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner, or delegated certificate installer, to install a - * certificate and corresponding private key. All apps within the profile will be able to access - * the certificate and use the private key, given direct user approval. + * This API can be called by the following to install a certificate and corresponding + * private key: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * All apps within the profile will be able to access the certificate and use the private key, + * given direct user approval. + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. However, this API sets the key pair as user selectable by default, + * which is not permitted when called by the credential management app. Instead, + * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be + * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag. * * <p>Access to the installed credentials will not be granted to the caller of this API without * direct user approval. This is for security - should a certificate installer become @@ -5225,10 +5286,23 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner, or delegated certificate installer, to install a - * certificate chain and corresponding private key for the leaf certificate. All apps within the - * profile will be able to access the certificate chain and use the private key, given direct - * user approval. + * This API can be called by the following to install a certificate chain and corresponding + * private key for the leaf certificate: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * All apps within the profile will be able to access the certificate chain and use the private + * key, given direct user approval. + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. However, this API sets the key pair as user selectable by default, + * which is not permitted when called by the credential management app. Instead, + * {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)} should be + * called with {@link #INSTALLKEY_SET_USER_SELECTABLE} not set as a flag. + * Note, there can only be a credential management app on an unmanaged device. * * <p>The caller of this API may grant itself access to the certificate and private key * immediately, without user approval. It is a best practice not to request this unless strictly @@ -5266,10 +5340,26 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner, or delegated certificate installer, to install a - * certificate chain and corresponding private key for the leaf certificate. All apps within the - * profile will be able to access the certificate chain and use the private key, given direct - * user approval (if the user is allowed to select the private key). + * This API can be called by the following to install a certificate chain and corresponding + * private key for the leaf certificate: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * All apps within the profile will be able to access the certificate chain and use the + * private key, given direct user approval (if the user is allowed to select the private key). + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. If called by the credential management app: + * <ul> + * <li>The componentName must be {@code null}r</li> + * <li>The alias must exist in the credential management app's + * {@link android.security.AppUriAuthenticationPolicy}</li> + * <li>The key pair must not be user selectable</li> + * </ul> + * Note, there can only be a credential management app on an unmanaged device. * * <p>The caller of this API may grant itself access to the certificate and private key * immediately, without user approval. It is a best practice not to request this unless strictly @@ -5295,7 +5385,8 @@ public class DevicePolicyManager { * {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}. * @return {@code true} if the keys were installed, {@code false} otherwise. * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile - * owner. + * owner, or {@code admin} is null but the calling application is not a delegated + * certificate installer or credential management app. * @see android.security.KeyChain#getCertificateChain * @see #setDelegatedScopes * @see #DELEGATION_CERT_INSTALL @@ -5328,15 +5419,26 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner, or delegated certificate installer, to remove a - * certificate and private key pair installed under a given alias. + * This API can be called by the following to remove a certificate and private key pair + * installed under a given alias: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. If called by the credential management app, the componentName must be + * {@code null}. Note, there can only be a credential management app on an unmanaged device. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. * @param alias The private key alias under which the certificate is installed. * @return {@code true} if the private key alias no longer exists, {@code false} otherwise. * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile - * owner. + * owner, or {@code admin} is null but the calling application is not a delegated + * certificate installer or credential management app. * @see #setDelegatedScopes * @see #DELEGATION_CERT_INSTALL */ @@ -5349,11 +5451,42 @@ public class DevicePolicyManager { } } + // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call. + /** + * Called by a device or profile owner, or delegated certificate installer, to query whether a + * certificate and private key are installed under a given alias. + * + * @param alias The alias under which the key pair is installed. + * @return {@code true} if a key pair with this alias exists, {@code false} otherwise. + * @throws SecurityException if the caller is not a device or profile owner or a delegated + * certificate installer. + * @see #setDelegatedScopes + * @see #DELEGATION_CERT_INSTALL + */ + public boolean hasKeyPair(@NonNull String alias) { + throwIfParentInstance("hasKeyPair"); + try { + return mService.hasKeyPair(mContext.getPackageName(), alias); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** - * Called by a device or profile owner, or delegated certificate installer, to generate a - * new private/public key pair. If the device supports key generation via secure hardware, - * this method is useful for creating a key in KeyChain that never left the secure hardware. - * Access to the key is controlled the same way as in {@link #installKeyPair}. + * This API can be called by the following to generate a new private/public key pair: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * If the device supports key generation via secure hardware, this method is useful for + * creating a key in KeyChain that never left the secure hardware. Access to the key is + * controlled the same way as in {@link #installKeyPair}. + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. If called by the credential management app, the componentName must be + * {@code null}. Note, there can only be a credential management app on an unmanaged device. * * <p>Because this method might take several seconds to complete, it should only be called from * a worker thread. This method returns {@code null} when called from the main thread. @@ -5376,9 +5509,10 @@ public class DevicePolicyManager { * supports these features, refer to {@link #isDeviceIdAttestationSupported()} and * {@link #isUniqueDeviceAttestationSupported()}. * - * <p>Device owner, profile owner and their delegated certificate installer can use - * {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device information - * including manufacturer, model, brand, device and product in the attestation record. + * <p>Device owner, profile owner, their delegated certificate installer and the credential + * management app can use {@link #ID_TYPE_BASE_INFO} to request inclusion of the general device + * information including manufacturer, model, brand, device and product in the attestation + * record. * Only device owner, profile owner on an organization-owned device and their delegated * certificate installers can use {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} and * {@link #ID_TYPE_MEID} to request unique device identifiers to be attested (the serial number, @@ -5413,9 +5547,11 @@ public class DevicePolicyManager { * {@code keySpec}. * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise. * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile - * owner. If Device ID attestation is requested (using {@link #ID_TYPE_SERIAL}, - * {@link #ID_TYPE_IMEI} or {@link #ID_TYPE_MEID}), the caller must be the Device Owner - * or the Certificate Installer delegate. + * owner, or {@code admin} is null but the calling application is not a delegated + * certificate installer or credential management app. If Device ID attestation is + * requested (using {@link #ID_TYPE_SERIAL}, {@link #ID_TYPE_IMEI} or + * {@link #ID_TYPE_MEID}), the caller must be the Device Owner or the Certificate + * Installer delegate. * @throws IllegalArgumentException in the following cases: * <p> * <ul> @@ -5578,10 +5714,19 @@ public class DevicePolicyManager { } /** - * Called by a device or profile owner, or delegated certificate installer, to associate - * certificates with a key pair that was generated using {@link #generateKeyPair}, and - * set whether the key is available for the user to choose in the certificate selection - * prompt. + * This API can be called by the following to associate certificates with a key pair that was + * generated using {@link #generateKeyPair}, and set whether the key is available for the user + * to choose in the certificate selection prompt: + * <ul> + * <li>Device owner</li> + * <li>Profile owner</li> + * <li>Delegated certificate installer</li> + * <li>Credential management app</li> + * </ul> + * + * <p>From Android {@link android.os.Build.VERSION_CODES#S}, the credential management app + * can call this API. If called by the credential management app, the componentName must be + * {@code null}. Note, there can only be a credential management app on an unmanaged device. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or * {@code null} if calling from a delegated certificate installer. @@ -5599,7 +5744,7 @@ public class DevicePolicyManager { * successfully associated with it, {@code false} otherwise. * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile * owner, or {@code admin} is null but the calling application is not a delegated - * certificate installer. + * certificate installer or credential management app. */ public boolean setKeyPairCertificate(@Nullable ComponentName admin, @NonNull String alias, @NonNull List<Certificate> certs, boolean isUserSelectable) { diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index cb879fce9c10..c02fcabfcf62 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -16,6 +16,7 @@ package android.app.admin; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; @@ -229,5 +230,11 @@ public abstract class DevicePolicyManagerInternal { /** * Returns the profile owner component for the given user, or {@code null} if there is not one. */ + @Nullable public abstract ComponentName getProfileOwnerAsUser(int userHandle); + + /** + * Returns whether the given package is a device owner or a profile owner in the calling user. + */ + public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 58368bc3779a..8be3cdc1296a 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -89,6 +89,7 @@ interface IDevicePolicyManager { int getPasswordComplexity(boolean parent); void setRequiredPasswordComplexity(int passwordComplexity, boolean parent); int getRequiredPasswordComplexity(boolean parent); + int getAggregatedPasswordComplexityForUser(int userId); boolean isUsingUnifiedPassword(in ComponentName admin); int getCurrentFailedPasswordAttempts(int userHandle, boolean parent); int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent); @@ -184,6 +185,7 @@ interface IDevicePolicyManager { in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess, boolean isUserSelectable); boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias); + boolean hasKeyPair(in String callerPackage, in String alias); boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec, in int idAttestationFlags, out KeymasterCertificateChain attestationChain); diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index 86f91d79ad2b..1cf45670ed93 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -85,7 +85,8 @@ public class SecurityLog { TAG_CRYPTO_SELF_TEST_COMPLETED, TAG_KEY_INTEGRITY_VIOLATION, TAG_CERT_VALIDATION_FAILURE, - TAG_CAMERA_POLICY_SET + TAG_CAMERA_POLICY_SET, + TAG_PASSWORD_COMPLEXITY_REQUIRED }) public @interface SecurityLogTag {} @@ -478,6 +479,21 @@ public class SecurityLog { SecurityLogTags.SECURITY_CAMERA_POLICY_SET; /** + * Indicates that an admin has set a password complexity requirement, using the platform's + * pre-defined complexity levels. The log entry contains the following information about the + * event, encapsulated in an {@link Object} array and accessible via + * {@link SecurityEvent#getData()}: + * <li> [0] admin package name ({@code String}) + * <li> [1] admin user ID ({@code Integer}) + * <li> [2] target user ID ({@code Integer}) + * <li> [3] Password complexity ({@code Integer}) + * + * @see DevicePolicyManager#setRequiredPasswordComplexity(int) + */ + public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = + SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED; + + /** * Event severity level indicating that the event corresponds to normal workflow. */ public static final int LEVEL_INFO = 1; @@ -617,6 +633,7 @@ public class SecurityLog { case TAG_USER_RESTRICTION_ADDED: case TAG_USER_RESTRICTION_REMOVED: case TAG_CAMERA_POLICY_SET: + case TAG_PASSWORD_COMPLEXITY_REQUIRED: return LEVEL_INFO; case TAG_CERT_AUTHORITY_REMOVED: case TAG_CRYPTO_SELF_TEST_COMPLETED: diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags index 100fd4cbd40f..db5245c919ab 100644 --- a/core/java/android/app/admin/SecurityLogTags.logtags +++ b/core/java/android/app/admin/SecurityLogTags.logtags @@ -1,4 +1,4 @@ -# See system/core/logcat/event.logtags for a description of the format of this file. +# See system/logging/logcat/event.logtags for a description of the format of this file. option java_package android.app.admin @@ -39,3 +39,4 @@ option java_package android.app.admin 210032 security_key_integrity_violation (key_id|3),(uid|1) 210033 security_cert_validation_failure (reason|3) 210034 security_camera_policy_set (package|3),(admin_user|1),(target_user|1),(disabled|1) +210035 security_password_complexity_required (package|3),(admin_user|1),(target_user|1),(complexity|1) diff --git a/core/java/android/app/people/PeopleSpaceTile.java b/core/java/android/app/people/PeopleSpaceTile.java index f5674e5cd0ce..e870597bf6d1 100644 --- a/core/java/android/app/people/PeopleSpaceTile.java +++ b/core/java/android/app/people/PeopleSpaceTile.java @@ -18,7 +18,12 @@ package android.app.people; import android.annotation.NonNull; import android.content.Intent; +import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Parcel; @@ -147,10 +152,10 @@ public class PeopleSpaceTile implements Parcelable { mPackageName = intent == null ? null : intent.getPackage(); } - public Builder(ShortcutInfo info) { + public Builder(ShortcutInfo info, LauncherApps launcherApps) { mId = info.getId(); mUserName = info.getLabel(); - mUserIcon = info.getIcon(); + mUserIcon = convertDrawableToIcon(launcherApps.getShortcutIconDrawable(info, 0)); mUid = info.getUserId(); mPackageName = info.getPackage(); } @@ -270,4 +275,32 @@ public class PeopleSpaceTile implements Parcelable { return new PeopleSpaceTile[size]; } }; + + /** Converts {@code drawable} to a {@link Icon}. */ + public static Icon convertDrawableToIcon(Drawable drawable) { + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + if (bitmapDrawable.getBitmap() != null) { + return Icon.createWithBitmap(bitmapDrawable.getBitmap()); + } + } + + Bitmap bitmap; + if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { + bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + // Single color bitmap will be created of 1x1 pixel + } else { + bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + } + + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + return Icon.createWithBitmap(bitmap); + } } diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java new file mode 100644 index 000000000000..3f00fa6f4181 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothLeAudio.java @@ -0,0 +1,451 @@ +/* + * Copyright 2020 HIMSA II K/S - www.himsa.com. + * Represented by EHIMA - www.ehima.com + * + * 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.bluetooth; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.content.Context; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.CloseGuard; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the public APIs to control the LeAudio profile. + * + * <p>BluetoothLeAudio is a proxy object for controlling the Bluetooth LE Audio + * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get + * the BluetoothLeAudio proxy object. + * + * <p> Android only supports one set of connected Bluetooth LeAudio device at a time. Each + * method is protected with its appropriate permission. + */ +public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { + private static final String TAG = "BluetoothLeAudio"; + private static final boolean DBG = false; + private static final boolean VDBG = false; + + private CloseGuard mCloseGuard; + + /** + * Intent used to broadcast the change in connection state of the LeAudio + * profile. Please note that in the binaural case, there will be two different LE devices for + * the left and right side and each device will have their own connection state changes. + * + * <p>This intent will have 3 extras: + * <ul> + * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> + * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> + * </ul> + * + * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of + * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, + * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED"; + + /** + * Intent used to broadcast the selection of a connected device as active. + * + * <p>This intent will have one extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * </ul> + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to + * receive. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = + "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; + + /** + * This represents an invalid group ID. + * + * @hide + */ + public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + + private BluetoothAdapter mAdapter; + private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = + new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", + IBluetoothLeAudio.class.getName()) { + @Override + public IBluetoothLeAudio getServiceInterface(IBinder service) { + return IBluetoothLeAudio.Stub.asInterface(Binder.allowBlocking(service)); + } + }; + + /** + * Create a BluetoothLeAudio proxy object for interacting with the local + * Bluetooth LeAudio service. + */ + /*package*/ BluetoothLeAudio(Context context, ServiceListener listener) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mProfileConnector.connect(context, listener); + mCloseGuard = new CloseGuard(); + mCloseGuard.open("close"); + } + + /** + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public void close() { + mProfileConnector.disconnect(); + } + + private IBluetoothLeAudio getService() { + return mProfileConnector.getService(); + } + + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } + close(); + } + + /** + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean connect(@Nullable BluetoothDevice device) { + if (DBG) log("connect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.connect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * + * @param device Remote Bluetooth Device + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean disconnect(@Nullable BluetoothDevice device) { + if (DBG) log("disconnect(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() && isValidDevice(device)) { + return service.disconnect(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List<BluetoothDevice> getConnectedDevices() { + if (VDBG) log("getConnectedDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getConnectedDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates( + @NonNull int[] states) { + if (VDBG) log("getDevicesMatchingStates()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getDevicesMatchingConnectionStates(states); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<BluetoothDevice>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<BluetoothDevice>(); + } + } + + /** + * {@inheritDoc} + */ + @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) + public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { + if (VDBG) log("getState(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionState(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + + /** + * Select a connected device as active. + * + * The active device selection is per profile. An active device's + * purpose is profile-specific. For example, LeAudio audio + * streaming is to the active LeAudio device. If a remote device + * is not connected, it cannot be selected as active. + * + * <p> This API returns false in scenarios like the profile on the + * device is not connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that the + * {@link #ACTION_LEAUDIO_ACTIVE_DEVICE_CHANGED} intent will be broadcasted + * with the active device. + * + * + * @param device the remote Bluetooth device. Could be null to clear + * the active device and stop streaming audio to a Bluetooth device. + * @return false on immediate error, true otherwise + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setActiveDevice(@Nullable BluetoothDevice device) { + if (DBG) log("setActiveDevice(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && ((device == null) || isValidDevice(device))) { + service.setActiveDevice(device); + return true; + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connected LeAudio devices that are active + * + * @return the list of active devices. Returns empty list on error. + * @hide + */ + @NonNull + @RequiresPermission(Manifest.permission.BLUETOOTH) + public List<BluetoothDevice> getActiveDevices() { + if (VDBG) log("getActiveDevices()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getActiveDevices(); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + + /** + * Get device group id. Devices with same group id belong to same group (i.e left and right + * earbud) + * @param device LE Audio capable device + * @return group id that this device currently belongs to + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH) + public int getGroupId(@NonNull BluetoothDevice device) { + if (VDBG) log("getGroupId()"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled()) { + return service.getGroupId(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return GROUP_ID_INVALID; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return GROUP_ID_INVALID; + } + } + + /** + * Set connection policy of the profile + * + * <p> The device should already be paired. + * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED}, + * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Paired bluetooth device + * @param connectionPolicy is the connection policy to set to for this profile + * @return true if connectionPolicy is set, false on error + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public boolean setConnectionPolicy(@NonNull BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { + if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN + && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return false; + } + } + + /** + * Get the connection policy of the profile. + * + * <p> The connection policy can be any of: + * {@link #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, + * {@link #CONNECTION_POLICY_UNKNOWN} + * + * @param device Bluetooth device + * @return connection policy of the device + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { + if (VDBG) log("getConnectionPolicy(" + device + ")"); + try { + final IBluetoothLeAudio service = getService(); + if (service != null && mAdapter.isEnabled() + && isValidDevice(device)) { + return service.getConnectionPolicy(device); + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } catch (RemoteException e) { + Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); + return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + } + } + + + /** + * Helper for converting a state to a string. + * + * For debug use only - strings are not internationalized. + * + * @hide + */ + public static String stateToString(int state) { + switch (state) { + case STATE_DISCONNECTED: + return "disconnected"; + case STATE_CONNECTING: + return "connecting"; + case STATE_CONNECTED: + return "connected"; + case STATE_DISCONNECTING: + return "disconnecting"; + default: + return "<unknown state " + state + ">"; + } + } + + private boolean isValidDevice(@Nullable BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + private static void log(String msg) { + Log.d(TAG, msg); + } +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index db851c4f33b9..c31b04e81456 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -207,12 +207,19 @@ public interface BluetoothProfile { int HEARING_AID = 21; /** + * LE Audio Device + * + * @hide + */ + int LE_AUDIO = 22; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * * @hide */ - int MAX_PROFILE_ID = 21; + int MAX_PROFILE_ID = 22; /** * Default priority for devices that we try to auto-connect to and diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index e35fb037582a..d7dc86a5c59f 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -63,7 +63,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; -import android.os.storage.StorageManager; import android.system.Int32Ref; import android.text.TextUtils; import android.util.EventLog; @@ -110,7 +109,7 @@ public abstract class ContentResolver implements ContentInterface { * * @hide */ - public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage(); + public static final boolean DEPRECATE_DATA_COLUMNS = true; /** * Special filesystem path prefix which indicates that a path should be diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING new file mode 100644 index 000000000000..a2880dfdfd17 --- /dev/null +++ b/core/java/android/content/TEST_MAPPING @@ -0,0 +1,52 @@ +{ + "presubmit": [ + { + "name": "CtsContentTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.wm.cts" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.content.ContextTest" + } + ], + "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"] + } + ] +}
\ No newline at end of file diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index 8b411d5fe031..b290679c9fcc 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -119,8 +119,9 @@ public class CrossProfileApps { * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a * {@link SecurityException} will be thrown. * @param callingActivity The activity to start the new activity from for the purposes of - * deciding which task the new activity should belong to. If {@code null}, the activity - * will always be started in a new task. + * passing back any result and deciding which task the new activity should belong to. If + * {@code null}, the activity will always be started in a new task and no result will be + * returned. */ @RequiresPermission(anyOf = { android.Manifest.permission.INTERACT_ACROSS_PROFILES, @@ -146,8 +147,9 @@ public class CrossProfileApps { * {@link #getTargetUserProfiles()} if different to the calling user, otherwise a * {@link SecurityException} will be thrown. * @param callingActivity The activity to start the new activity from for the purposes of - * deciding which task the new activity should belong to. If {@code null}, the activity - * will always be started in a new task. + * passing back any result and deciding which task the new activity should belong to. If + * {@code null}, the activity will always be started in a new task and no result will be + * returned. * @param options The activity options or {@code null}. See {@link android.app.ActivityOptions}. */ @RequiresPermission(anyOf = { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 044b3b2e8284..d1c4ae9c57c7 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -98,6 +98,11 @@ import java.util.Set; * packages that are currently installed on the device. * * You can find this class through {@link Context#getPackageManager}. + * + * <p class="note"><strong>Note: </strong>If your app targets Android 11 (API level 30) or + * higher, the methods in this class each return a filtered list of apps. Learn more about how to + * <a href="/training/basics/intents/package-visibility">manage package visibility</a>. + * </p> */ public abstract class PackageManager { private static final String TAG = "PackageManager"; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 32900225d887..ca5eeb1863c7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -911,38 +911,77 @@ public final class DisplayManager { public interface DeviceConfig { /** - * Key for refresh rate in the zone defined by thresholds. + * Key for refresh rate in the low zone defined by thresholds. * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.integer#config_defaultZoneBehavior */ - String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone"; + String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone"; /** - * Key for accessing the display brightness thresholds for the configured refresh rate zone. + * Key for accessing the low display brightness thresholds for the configured refresh + * rate zone. * The value will be a pair of comma separated integers representing the minimum and maximum * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]). * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. + * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate * @hide */ - String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS = + String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS = "peak_refresh_rate_brightness_thresholds"; /** - * Key for accessing the ambient brightness thresholds for the configured refresh rate zone. - * The value will be a pair of comma separated integers representing the minimum and maximum - * thresholds of the zone, respectively, in lux. + * Key for accessing the low ambient brightness thresholds for the configured refresh + * rate zone. The value will be a pair of comma separated integers representing the minimum + * and maximum thresholds of the zone, respectively, in lux. + * + * Note that the name and value don't match because they were added before we had a high + * zone to consider. * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate * @hide */ - String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS = + String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS = "peak_refresh_rate_ambient_thresholds"; + /** + * Key for refresh rate in the high zone defined by thresholds. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.integer#config_fixedRefreshRateInHighZone + */ + String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone"; /** + * Key for accessing the display brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]). + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate + * @hide + */ + String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS = + "fixed_refresh_rate_high_display_brightness_thresholds"; + + /** + * Key for accessing the ambient brightness thresholds for the configured refresh rate zone. + * The value will be a pair of comma separated integers representing the minimum and maximum + * thresholds of the zone, respectively, in lux. + * + * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER + * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate + * @hide + */ + String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS = + "fixed_refresh_rate_high_ambient_brightness_thresholds"; + /** * Key for default peak refresh rate * * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER diff --git a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java index 3fd20f12381e..c6007f1184f5 100644 --- a/core/java/android/hardware/hdmi/HdmiDeviceInfo.java +++ b/core/java/android/hardware/hdmi/HdmiDeviceInfo.java @@ -513,4 +513,19 @@ public class HdmiDeviceInfo implements Parcelable { && mDeviceId == other.mDeviceId && mAdopterId == other.mAdopterId; } + + @Override + public int hashCode() { + return java.util.Objects.hash( + mHdmiDeviceType, + mPhysicalAddress, + mPortId, + mLogicalAddress, + mDeviceType, + mVendorId, + mDevicePowerStatus, + mDisplayName, + mDeviceId, + mAdopterId); + } } diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 7cf0b10031ac..3cd13a212a4b 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -199,10 +199,11 @@ public abstract class AbstractInputMethodService extends Service * Dumps the internal state of IME to a protocol buffer output stream. * * @param proto ProtoOutputStream to dump data to. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @SuppressWarnings("HiddenAbstractMethod") - public abstract void dumpProtoInternal(ProtoOutputStream proto); + public abstract void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto); /** * Implement this to handle {@link android.os.Binder#dump Binder.dump()} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4a5d831cd705..5576857d1f6b 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -25,6 +25,7 @@ import static android.inputmethodservice.InputMethodServiceProto.EXTRACTED_TOKEN import static android.inputmethodservice.InputMethodServiceProto.EXTRACT_VIEW_HIDDEN; import static android.inputmethodservice.InputMethodServiceProto.FULLSCREEN_APPLIED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_BINDING; +import static android.inputmethodservice.InputMethodServiceProto.INPUT_CONNECTION_CALL; import static android.inputmethodservice.InputMethodServiceProto.INPUT_EDITOR_INFO; import static android.inputmethodservice.InputMethodServiceProto.INPUT_STARTED; import static android.inputmethodservice.InputMethodServiceProto.INPUT_VIEW_STARTED; @@ -585,10 +586,12 @@ public class InputMethodService extends AbstractInputMethodService { Log.w(TAG, "The token has already registered, ignore this initialization."); return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); attachToken(token); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -642,6 +645,7 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void bindInput(InputBinding binding) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.bindInput"); mInputBinding = binding; mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding @@ -649,6 +653,7 @@ public class InputMethodService extends AbstractInputMethodService { reportFullscreenMode(); initialize(); onBindInput(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -686,7 +691,9 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void restartInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput"); doStartInput(ic, attribute, true); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -736,7 +743,8 @@ public class InputMethodService extends AbstractInputMethodService { return; } ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#hideSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput"); @@ -792,7 +800,8 @@ public class InputMethodService extends AbstractInputMethodService { } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showSoftInput"); ImeTracing.getInstance().triggerServiceDump( - "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this); + "InputMethodService.InputMethodImpl#showSoftInput", InputMethodService.this, + null /* icProto */); final boolean wasVisible = isInputViewShown(); if (dispatchOnShowInputRequested(flags, false)) { @@ -1253,6 +1262,7 @@ public class InputMethodService extends AbstractInputMethodService { } @Override public void onCreate() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate"); mTheme = Resources.selectSystemTheme(mTheme, getApplicationInfo().targetSdkVersion, android.R.style.Theme_InputMethod, @@ -1273,6 +1283,7 @@ public class InputMethodService extends AbstractInputMethodService { // in non-default display. mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow"); mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); @@ -1294,10 +1305,12 @@ public class InputMethodService extends AbstractInputMethodService { initViews(); mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); mInlineSuggestionSessionController = new InlineSuggestionSessionController( this::onCreateInlineSuggestionsRequest, this::getHostInputToken, this::onInlineSuggestionsResponse); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1318,6 +1331,7 @@ public class InputMethodService extends AbstractInputMethodService { } void initViews() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews"); mInitialized = false; mViewsCreated = false; mShowInputRequested = false; @@ -1352,6 +1366,7 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesVisibility = getCandidatesHiddenVisibility(); mCandidatesFrame.setVisibility(mCandidatesVisibility); mInputFrame.setVisibility(View.GONE); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override public void onDestroy() { @@ -1393,6 +1408,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void resetStateForNewConfiguration() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.resetStateForNewConfiguration"); boolean visible = mDecorViewVisible; int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; @@ -1428,6 +1444,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean showing = onEvaluateInputViewShown(); setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1589,6 +1606,7 @@ public class InputMethodService extends AbstractInputMethodService { * is currently running in fullscreen mode. */ public void updateFullscreenMode() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.updateFullscreenMode"); boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); boolean changed = mLastShowInputRequested != mShowInputRequested; if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { @@ -1627,6 +1645,7 @@ public class InputMethodService extends AbstractInputMethodService { onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested); mLastShowInputRequested = mShowInputRequested; } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1755,6 +1774,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param outInsets Fill in with the current UI insets. */ public void onComputeInsets(Insets outInsets) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onComputeInsets"); int[] loc = mTmpLocation; if (mInputFrame.getVisibility() == View.VISIBLE) { mInputFrame.getLocationInWindow(loc); @@ -1775,6 +1795,7 @@ public class InputMethodService extends AbstractInputMethodService { outInsets.visibleTopInsets = loc[1]; outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; outInsets.touchableRegion.setEmpty(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -2164,8 +2185,9 @@ public class InputMethodService extends AbstractInputMethodService { return; } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this); - + ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this, + null /* icProto */); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; final int previousImeWindowStatus = @@ -2189,6 +2211,7 @@ public class InputMethodService extends AbstractInputMethodService { } mDecorViewWasVisible = true; mInShowWindow = false; + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -2241,7 +2264,8 @@ public class InputMethodService extends AbstractInputMethodService { */ private void applyVisibilityInInsetsConsumerIfNecessary(boolean setVisible) { ImeTracing.getInstance().triggerServiceDump( - "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this); + "InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", this, + null /* icProto */); mPrivOps.applyImeVisibility(setVisible ? mCurShowInputToken : mCurHideInputToken, setVisible); } @@ -2266,7 +2290,8 @@ public class InputMethodService extends AbstractInputMethodService { public void hideWindow() { if (DEBUG) Log.v(TAG, "CALL: hideWindow"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", this, + null /* icProto */); mWindowVisible = false; finishViews(false /* finishingInput */); if (mDecorViewVisible) { @@ -2337,7 +2362,8 @@ public class InputMethodService extends AbstractInputMethodService { void doFinishInput() { if (DEBUG) Log.v(TAG, "CALL: doFinishInput"); - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doFinishInput", this, + null /* icProto */); finishViews(true /* finishingInput */); if (mInputStarted) { mInlineSuggestionSessionController.notifyOnFinishInput(); @@ -2353,7 +2379,8 @@ public class InputMethodService extends AbstractInputMethodService { if (!restarting && mInputStarted) { doFinishInput(); } - ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#doStartInput", this, + null /* icProto */); mInputStarted = true; mStartedInputConnection = ic; mInputEditorInfo = attribute; @@ -2512,7 +2539,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public void requestHideSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", this, + null /* icProto */); mPrivOps.hideMySoftInput(flags); } @@ -2525,7 +2553,8 @@ public class InputMethodService extends AbstractInputMethodService { * @param flags Provides additional operating flags. */ public final void requestShowSelf(int flags) { - ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this); + ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", this, + null /* icProto */); mPrivOps.showMySoftInput(flags); } @@ -3345,7 +3374,7 @@ public class InputMethodService extends AbstractInputMethodService { * @hide */ @Override - public final void dumpProtoInternal(ProtoOutputStream proto) { + public final void dumpProtoInternal(ProtoOutputStream proto, ProtoOutputStream icProto) { final long token = proto.start(InputMethodServiceTraceProto.INPUT_METHOD_SERVICE); mWindow.dumpDebug(proto, SOFT_INPUT_WINDOW); proto.write(VIEWS_CREATED, mViewsCreated); @@ -3374,6 +3403,9 @@ public class InputMethodService extends AbstractInputMethodService { proto.write(STATUS_ICON, mStatusIcon); mTmpInsets.dumpDebug(proto, LAST_COMPUTED_INSETS); proto.write(SETTINGS_OBSERVER, Objects.toString(mSettingsObserver)); + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } proto.end(token); } } diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java index c2586fa0c825..269bbf20c8b1 100644 --- a/core/java/android/net/CaptivePortal.java +++ b/core/java/android/net/CaptivePortal.java @@ -15,7 +15,6 @@ */ package android.net; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -24,8 +23,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - /** * A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN} * activity to indicate to the system different outcomes of captive portal sign in. This class is @@ -75,17 +72,6 @@ public class CaptivePortal implements Parcelable { private final IBinder mBinder; /** @hide */ - @IntDef(value = { - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED, - MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS, - MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR, - }) - public @interface EventId { - } - - /** @hide */ public CaptivePortal(@NonNull IBinder binder) { mBinder = binder; } @@ -176,7 +162,7 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - public void logEvent(@EventId int eventId, @NonNull String packageName) { + public void logEvent(int eventId, @NonNull String packageName) { try { ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName); } catch (RemoteException e) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 4b38d6377f37..3f2c966f9ed2 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -684,7 +684,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage + @SystemApi public static final int TYPE_PROXY = 16; /** diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java index d31218d9b67b..a17a49897d39 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -51,13 +51,6 @@ public class NetworkProvider { public static final int ID_NONE = -1; /** - * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any - * provider, so they use this constant for clarity instead of NONE. - * @hide only used by ConnectivityService. - */ - public static final int ID_VPN = -2; - - /** * The first providerId value that will be allocated. * @hide only used by ConnectivityService. */ diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index d130bc5d37e7..b951aca6d680 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -67,7 +67,7 @@ public final class PowerManager { /* NOTE: Wake lock levels were previously defined as a bit field, except that only a few * combinations were actually supported so the bit field was removed. This explains * why the numbering scheme is so odd. If adding a new wake lock level, any unused - * value (in frameworks/base/core/proto/android/os/enums.proto) can be used. + * value (in frameworks/proto_logging/stats/enums/os/enums.proto) can be used. */ /** diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 0f2a9f21a0bc..f76eb86f0fca 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -1138,8 +1138,7 @@ public abstract class VibrationEffect implements Parcelable { * * @param primitiveId The primitive to add * @param scale The scale to apply to the intensity of the primitive. - * @param delay The amount of time, in milliseconds, to wait between playing the prior - * primitive and this one + * @param delay The amount of time in milliseconds to wait before playing this primitive * @return The {@link Composition} object to enable adding multiple primitives in one chain. */ @NonNull diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index cfc3e01c23c5..870d224f2ff7 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -156,10 +156,6 @@ public class StorageManager { /** {@hide} */ public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk"; /** {@hide} */ - public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage"; - /** {@hide} */ - public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; - /** {@hide} */ public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST = "forced_scoped_storage_whitelist"; @@ -263,10 +259,6 @@ public class StorageManager { public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4; /** {@hide} */ public static final int DEBUG_VIRTUAL_DISK = 1 << 5; - /** {@hide} */ - public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6; - /** {@hide} */ - public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7; /** {@hide} */ public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; @@ -1695,16 +1687,13 @@ public class StorageManager { /** * Return if the currently booted device has the "isolated storage" feature - * flag enabled. This will eventually be fully enabled in the final - * {@link android.os.Build.VERSION_CODES#Q} release. + * flag enabled. * * @hide */ @SystemApi public static boolean hasIsolatedStorage() { - // Prefer to use snapshot for current boot when available - return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, - SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true)); + return false; } /** diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 4379ce1055ef..55ba15a5e57b 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -28,50 +28,8 @@ import java.util.Set; * @hide Only for use within the system server. */ public abstract class StorageManagerInternal { - - /** - * Policy that influences how external storage is mounted and reported. - */ - public interface ExternalStorageMountPolicy { - /** - * Gets the external storage mount mode for the given uid. - * - * @param uid The UID for which to determine mount mode. - * @param packageName The package in the UID for making the call. - * @return The mount mode. - * - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_NONE - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_DEFAULT - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_READ - * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_WRITE - */ - public int getMountMode(int uid, String packageName); - - /** - * Gets whether external storage should be reported to the given UID. - * - * @param uid The UID for which to determine whether it has external storage. - * @param packageName The package in the UID for making the call. - * @return Weather to report external storage. - * @return True to report the state of external storage, false to - * report it as unmounted. - */ - public boolean hasExternalStorage(int uid, String packageName); - } - - /** - * Adds a policy for determining how external storage is mounted and reported. - * The mount mode is the most conservative result from querying all registered - * policies. Similarly, the reported state is the most conservative result from - * querying all registered policies. - * - * @param policy The policy to add. - */ - public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy); - /** - * Gets the mount mode to use for a given UID as determined by consultin all - * policies. + * Gets the mount mode to use for a given UID * * @param uid The UID for which to get mount mode. * @param packageName The package in the UID for making the call. diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index 901494b845b0..237a9f2867d6 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -200,6 +200,21 @@ public class VolumeInfo implements Parcelable { internalPath = parcel.readString8(); } + public VolumeInfo(VolumeInfo volumeInfo) { + this.id = volumeInfo.id; + this.type = volumeInfo.type; + this.disk = volumeInfo.disk; + this.partGuid = volumeInfo.partGuid; + this.mountFlags = volumeInfo.mountFlags; + this.mountUserId = volumeInfo.mountUserId; + this.state = volumeInfo.state; + this.fsType = volumeInfo.fsType; + this.fsUuid = volumeInfo.fsUuid; + this.fsLabel = volumeInfo.fsLabel; + this.path = volumeInfo.path; + this.internalPath = volumeInfo.internalPath; + } + @UnsupportedAppUsage public static @NonNull String getEnvironmentForState(int state) { final String envState = sStateToEnvironment.get(state); diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 714bcea9c01f..44cc0f5c330d 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -451,6 +451,22 @@ public final class DeviceConfig { "connectivity_thermal_power_manager"; /** + * Namespace for all statsd native features that can be applied immediately. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_STATSD_NATIVE = "statsd_native"; + + /** + * Namespace for all statsd native features that are applied on boot. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot"; + + /** * Namespace for configuration related features. * * @hide diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 884f8ccd5e54..b86b9ff814ae 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9797,12 +9797,13 @@ public final class Settings { "use_blast_adapter_sv"; /** - * If {@code true}, vendor provided window manager display settings will be ignored. - * (0 = false, 1 = true) + * Path to the WindowManager display settings file. If unset, the default file path will + * be used. + * * @hide */ - public static final String DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS = - "ignore_vendor_display_settings"; + public static final String DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH = + "wm_display_settings_path"; /** * Whether user has enabled development settings. @@ -13286,16 +13287,6 @@ public final class Settings { "storage_settings_clobber_threshold"; /** - * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF} - * temporarily for all users. - * - * @hide - */ - @TestApi - public static final String LOCATION_GLOBAL_KILL_SWITCH = - "location_global_kill_switch"; - - /** * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored * and restoring to lower version of platform API will be skipped. * @@ -13415,11 +13406,6 @@ public final class Settings { public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY = "max_sound_trigger_detection_service_ops_per_day"; - /** {@hide} */ - public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local"; - /** {@hide} */ - public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote"; - /** * Indicates whether aware is available in the current location. * @hide diff --git a/core/java/android/service/autofill/TEST_MAPPING b/core/java/android/service/autofill/TEST_MAPPING new file mode 100644 index 000000000000..87a17ebee36d --- /dev/null +++ b/core/java/android/service/autofill/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/services/autofill/java/com/android/server/autofill" + } + ] +} diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 9a76f19f3e41..37637119c70a 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -199,6 +199,7 @@ public abstract class WallpaperService extends Service { final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0]; final MergedConfiguration mMergedConfiguration = new MergedConfiguration(); private final Point mSurfaceSize = new Point(); + private final Point mLastSurfaceSize = new Point(); private final Matrix mTmpMatrix = new Matrix(); private final float[] mTmpValues = new float[9]; @@ -908,6 +909,14 @@ public abstract class WallpaperService extends Service { if (mSurfaceControl.isValid()) { mSurfaceHolder.mSurface.copyFrom(mSurfaceControl); } + if (!mLastSurfaceSize.equals(mSurfaceSize)) { + mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y); + if (mSurfaceControl != null && mSurfaceControl.isValid()) { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + t.setBufferSize(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y); + t.apply(); + } + } if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrames); diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java index 6d5e830346cd..ece6b3516f7a 100644 --- a/core/java/android/util/DebugUtils.java +++ b/core/java/android/util/DebugUtils.java @@ -271,6 +271,26 @@ public class DebugUtils { return res.toString(); } + /** + * Gets human-readable representation of constants (static final values). + * + * @hide + */ + public static String constantToString(Class<?> clazz, String prefix, int value) { + for (Field field : clazz.getDeclaredFields()) { + final int modifiers = field.getModifiers(); + try { + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) + && field.getType().equals(int.class) && field.getName().startsWith(prefix) + && field.getInt(null) == value) { + return constNameWithoutPrefix(prefix, field); + } + } catch (IllegalAccessException ignored) { + } + } + return prefix + Integer.toString(value); + } + private static String constNameWithoutPrefix(String prefix, Field field) { return field.getName().substring(prefix.length()); } diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java index 4058eef3e2a3..723f1dd15e23 100644 --- a/core/java/android/util/imetracing/ImeTracing.java +++ b/core/java/android/util/imetracing/ImeTracing.java @@ -110,15 +110,20 @@ public abstract class ImeTracing { * * @param where Place where the trace was triggered. * @param immInstance The {@link InputMethodManager} instance to dump. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerClientDump(String where, InputMethodManager immInstance); + public abstract void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto); /** * Starts a proto dump of the currently connected InputMethodService information. * * @param where Place where the trace was triggered. + * @param service The {@link android.inputmethodservice.InputMethodService} to be dumped. + * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. */ - public abstract void triggerServiceDump(String where, AbstractInputMethodService service); + public abstract void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto); /** * Starts a proto dump of the InputMethodManagerService information. diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java index 904b44da97d7..6cc652d942cc 100644 --- a/core/java/android/util/imetracing/ImeTracingClientImpl.java +++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java @@ -45,7 +45,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, @NonNull InputMethodManager immInstance) { + public void triggerClientDump(String where, @NonNull InputMethodManager immInstance, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -59,7 +60,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - immInstance.dumpDebug(proto); + immInstance.dumpDebug(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_CLIENT, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related client dump to server", e); @@ -69,7 +70,8 @@ class ImeTracingClientImpl extends ImeTracing { } @Override - public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service) { + public void triggerServiceDump(String where, @NonNull AbstractInputMethodService service, + ProtoOutputStream icProto) { if (!isEnabled() || !isAvailable()) { return; } @@ -83,7 +85,7 @@ class ImeTracingClientImpl extends ImeTracing { try { ProtoOutputStream proto = new ProtoOutputStream(); - service.dumpProtoInternal(proto); + service.dumpProtoInternal(proto, icProto); sendToService(proto.getBytes(), IME_TRACING_FROM_IMS, where); } catch (RemoteException e) { Log.e(TAG, "Exception while sending ime-related service dump to server", e); diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java index d758d77fb2f2..e793c280afbc 100644 --- a/core/java/android/util/imetracing/ImeTracingServerImpl.java +++ b/core/java/android/util/imetracing/ImeTracingServerImpl.java @@ -133,12 +133,14 @@ class ImeTracingServerImpl extends ImeTracing { } @Override - public void triggerClientDump(String where, InputMethodManager immInstance) { + public void triggerClientDump(String where, InputMethodManager immInstance, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } @Override - public void triggerServiceDump(String where, AbstractInputMethodService service) { + public void triggerServiceDump(String where, AbstractInputMethodService service, + ProtoOutputStream icProto) { // Intentionally left empty, this is implemented in ImeTracingClientImpl } diff --git a/core/java/android/util/imetracing/InputConnectionHelper.java b/core/java/android/util/imetracing/InputConnectionHelper.java new file mode 100644 index 000000000000..39f1e01eb4a9 --- /dev/null +++ b/core/java/android/util/imetracing/InputConnectionHelper.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.imetracing; + +import static android.view.inputmethod.InputConnectionCallProto.GET_CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionCallProto.GET_EXTRACTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_SURROUNDING_TEXT; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_AFTER_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GET_TEXT_BEFORE_CURSOR; +import static android.view.inputmethod.InputConnectionCallProto.GetExtractedText.REQUEST; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.util.proto.ProtoOutputStream; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnectionCallProto.GetCursorCapsMode; +import android.view.inputmethod.InputConnectionCallProto.GetExtractedText; +import android.view.inputmethod.InputConnectionCallProto.GetSelectedText; +import android.view.inputmethod.InputConnectionCallProto.GetSurroundingText; +import android.view.inputmethod.InputConnectionCallProto.GetTextAfterCursor; +import android.view.inputmethod.InputConnectionCallProto.GetTextBeforeCursor; +import android.view.inputmethod.SurroundingText; + +/** + * Helper class for constructing {@link android.view.inputmethod.InputConnection} dumps, which are + * integrated into {@link ImeTracing}. + * @hide + */ +public class InputConnectionHelper { + static final String TAG = "InputConnectionHelper"; + public static final boolean DUMP_TEXT = false; + + private InputConnectionHelper() {} + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextAfterCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text after the cursor position; the length of the + * returned text might be less than <var>length</var>. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextAfterCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_AFTER_CURSOR); + proto.write(GetTextAfterCursor.LENGTH, length); + proto.write(GetTextAfterCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextAfterCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextAfterCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getTextBeforeCursor(int, int)} data. + * + * @param length The expected length of the text. This must be non-negative. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result The text before the cursor position; the length of the + * returned text might be less than <var>length</var>. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetTextBeforeCursorProto(@IntRange(from = 0) int length, + int flags, @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_TEXT_BEFORE_CURSOR); + proto.write(GetTextBeforeCursor.LENGTH, length); + proto.write(GetTextBeforeCursor.FLAGS, flags); + if (result == null) { + proto.write(GetTextBeforeCursor.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetTextBeforeCursor.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSelectedText(int)} data. + * + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result the text that is currently selected, if any, or null if + * no text is selected. In {@link android.os.Build.VERSION_CODES#N} and + * later, returns false when the target application does not implement + * this method. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSelectedTextProto(int flags, + @Nullable CharSequence result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SELECTED_TEXT); + proto.write(GetSelectedText.FLAGS, flags); + if (result == null) { + proto.write(GetSelectedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetSelectedText.RESULT, result.toString()); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getSurroundingText(int, int, int)} data. + * + * @param beforeLength The expected length of the text before the cursor. + * @param afterLength The expected length of the text after the cursor. + * @param flags Supplies additional options controlling how the text is + * returned. May be either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_TEXT_WITH_STYLES}. + * @param result an {@link android.view.inputmethod.SurroundingText} object describing the + * surrounding text and state of selection, or null if the input connection is no longer valid, + * or the editor can't comply with the request for some reason, or the application does not + * implement this method. The length of the returned text might be less than the sum of + * <var>beforeLength</var> and <var>afterLength</var> . + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetSurroundingTextProto(@IntRange(from = 0) + int beforeLength, @IntRange(from = 0) int afterLength, int flags, + @Nullable SurroundingText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_SURROUNDING_TEXT); + proto.write(GetSurroundingText.BEFORE_LENGTH, beforeLength); + proto.write(GetSurroundingText.AFTER_LENGTH, afterLength); + proto.write(GetSurroundingText.FLAGS, flags); + if (result == null) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, "null result"); + proto.end(token_result); + } else if (DUMP_TEXT) { + final long token_result = proto.start(GetSurroundingText.RESULT); + proto.write(GetSurroundingText.SurroundingText.TEXT, result.getText().toString()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_START, + result.getSelectionStart()); + proto.write(GetSurroundingText.SurroundingText.SELECTION_END, + result.getSelectionEnd()); + proto.write(GetSurroundingText.SurroundingText.OFFSET, result.getOffset()); + proto.end(token_result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getCursorCapsMode(int)} data. + * + * @param reqModes The desired modes to retrieve, as defined by + * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}. + * @param result the caps mode flags that are in effect at the current + * cursor position. See TYPE_TEXT_FLAG_CAPS_* in {@link android.text.InputType}. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetCursorCapsModeProto(int reqModes, int result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_CURSOR_CAPS_MODE); + proto.write(GetCursorCapsMode.REQ_MODES, reqModes); + if (DUMP_TEXT) { + proto.write(GetCursorCapsMode.RESULT, result); + } + proto.end(token); + return proto; + } + + /** + * Builder for InputConnectionCallProto to hold + * {@link android.view.inputmethod.InputConnection#getExtractedText(ExtractedTextRequest, int)} + * data. + * + * @param request Description of how the text should be returned. + * {@link android.view.inputmethod.ExtractedTextRequest} + * @param flags Additional options to control the client, either {@code 0} or + * {@link android.view.inputmethod.InputConnection#GET_EXTRACTED_TEXT_MONITOR}. + * @param result an {@link android.view.inputmethod.ExtractedText} + * object describing the state of the text view and containing the + * extracted text itself, or null if the input connection is no + * longer valid of the editor can't comply with the request for + * some reason. + * @return ProtoOutputStream holding the InputConnectionCallProto data. + */ + public static ProtoOutputStream buildGetExtractedTextProto(@NonNull ExtractedTextRequest + request, int flags, @Nullable ExtractedText result) { + ProtoOutputStream proto = new ProtoOutputStream(); + final long token = proto.start(GET_EXTRACTED_TEXT); + final long token_request = proto.start(REQUEST); + proto.write(GetExtractedText.ExtractedTextRequest.TOKEN, request.token); + proto.write(GetExtractedText.ExtractedTextRequest.FLAGS, request.flags); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_LINES, request.hintMaxLines); + proto.write(GetExtractedText.ExtractedTextRequest.HINT_MAX_CHARS, request.hintMaxChars); + proto.end(token_request); + proto.write(GetExtractedText.FLAGS, flags); + if (result == null) { + proto.write(GetExtractedText.RESULT, "null result"); + } else if (DUMP_TEXT) { + proto.write(GetExtractedText.RESULT, result.text.toString()); + } + proto.end(token); + return proto; + } +} diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java new file mode 100644 index 000000000000..b58937beed55 --- /dev/null +++ b/core/java/android/view/ContentInfo.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ClipData; +import android.net.Uri; +import android.os.Bundle; +import android.util.ArrayMap; + +import com.android.internal.util.Preconditions; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Holds all the relevant data for a request to {@link View#performReceiveContent}. + */ +public final class ContentInfo { + + /** + * Specifies the UI through which content is being inserted. Future versions of Android may + * support additional values. + * + * @hide + */ + @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, + SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Source {} + + /** + * Specifies that the operation was triggered by the app that contains the target view. + */ + public static final int SOURCE_APP = 0; + + /** + * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or + * "Paste as plain text" action in the insertion/selection menu). + */ + public static final int SOURCE_CLIPBOARD = 1; + + /** + * Specifies that the operation was triggered from the soft keyboard (also known as input + * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard + * for more info. + */ + public static final int SOURCE_INPUT_METHOD = 2; + + /** + * Specifies that the operation was triggered by the drag/drop framework. See + * https://developer.android.com/guide/topics/ui/drag-drop for more info. + */ + public static final int SOURCE_DRAG_AND_DROP = 3; + + /** + * Specifies that the operation was triggered by the autofill framework. See + * https://developer.android.com/guide/topics/text/autofill for more info. + */ + public static final int SOURCE_AUTOFILL = 4; + + /** + * Specifies that the operation was triggered by a result from a + * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection + * menu. + */ + public static final int SOURCE_PROCESS_TEXT = 5; + + /** + * Returns the symbolic name of the given source. + * + * @hide + */ + static String sourceToString(@Source int source) { + switch (source) { + case SOURCE_APP: return "SOURCE_APP"; + case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; + case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; + case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; + case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; + case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; + } + return String.valueOf(source); + } + + /** + * Flags to configure the insertion behavior. + * + * @hide + */ + @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Flags {} + + /** + * Flag requesting that the content should be converted to plain text prior to inserting. + */ + public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; + + /** + * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. + * + * @hide + */ + static String flagsToString(@Flags int flags) { + if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { + return "FLAG_CONVERT_TO_PLAIN_TEXT"; + } + return String.valueOf(flags); + } + + @NonNull + private final ClipData mClip; + @Source + private final int mSource; + @Flags + private final int mFlags; + @Nullable + private final Uri mLinkUri; + @Nullable + private final Bundle mExtras; + + private ContentInfo(Builder b) { + this.mClip = Objects.requireNonNull(b.mClip); + this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, + "source"); + this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); + this.mLinkUri = b.mLinkUri; + this.mExtras = b.mExtras; + } + + @NonNull + @Override + public String toString() { + return "ContentInfo{" + + "clip=" + mClip + + ", source=" + sourceToString(mSource) + + ", flags=" + flagsToString(mFlags) + + ", linkUri=" + mLinkUri + + ", extras=" + mExtras + + "}"; + } + + /** + * The data to be inserted. + */ + @NonNull + public ClipData getClip() { + return mClip; + } + + /** + * The source of the operation. See {@code SOURCE_} constants. Future versions of Android + * may pass additional values. + */ + @Source + public int getSource() { + return mSource; + } + + /** + * Optional flags that control the insertion behavior. See {@code FLAG_} constants. + */ + @Flags + public int getFlags() { + return mFlags; + } + + /** + * Optional http/https URI for the content that may be provided by the IME. This is only + * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty + * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the + * IME. + */ + @Nullable + public Uri getLinkUri() { + return mLinkUri; + } + + /** + * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will + * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by + * the IME. + */ + @Nullable + public Bundle getExtras() { + return mExtras; + } + + /** + * Partitions the content based on the given predicate. + * + * <p>Similar to a + * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector}, + * this function classifies the content and organizes it into a map, grouping the items that + * matched vs didn't match the predicate. + * + * <p>Except for the {@link ClipData} items, the returned objects will contain all the same + * metadata as the original. + * + * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which + * partition to place it into. + * @return A map containing the partitioned content. The map will contain a single entry if + * all items were classified into the same partition (all matched or all didn't match the + * predicate) or two entries (if there's at least one item that matched the predicate and at + * least one item that didn't match the predicate). + */ + @NonNull + public Map<Boolean, ContentInfo> partition(@NonNull Predicate<ClipData.Item> itemPredicate) { + if (mClip.getItemCount() == 1) { + Map<Boolean, ContentInfo> result = new ArrayMap<>(1); + result.put(itemPredicate.test(mClip.getItemAt(0)), this); + return result; + } + ArrayList<ClipData.Item> accepted = new ArrayList<>(); + ArrayList<ClipData.Item> remaining = new ArrayList<>(); + for (int i = 0; i < mClip.getItemCount(); i++) { + ClipData.Item item = mClip.getItemAt(i); + if (itemPredicate.test(item)) { + accepted.add(item); + } else { + remaining.add(item); + } + } + Map<Boolean, ContentInfo> result = new ArrayMap<>(2); + if (!accepted.isEmpty()) { + ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted); + result.put(true, new Builder(this).setClip(acceptedClip).build()); + } + if (!remaining.isEmpty()) { + ClipData remainingClip = new ClipData(mClip.getDescription(), remaining); + result.put(false, new Builder(this).setClip(remainingClip).build()); + } + return result; + } + + /** + * Builder for {@link ContentInfo}. + */ + public static final class Builder { + @NonNull + private ClipData mClip; + @Source + private int mSource; + @Flags + private int mFlags; + @Nullable + private Uri mLinkUri; + @Nullable + private Bundle mExtras; + + /** + * Creates a new builder initialized with the data from the given builder. + */ + public Builder(@NonNull ContentInfo other) { + mClip = other.mClip; + mSource = other.mSource; + mFlags = other.mFlags; + mLinkUri = other.mLinkUri; + mExtras = other.mExtras; + } + + /** + * Creates a new builder. + * @param clip The data to insert. + * @param source The source of the operation. See {@code SOURCE_} constants. + */ + public Builder(@NonNull ClipData clip, @Source int source) { + mClip = clip; + mSource = source; + } + + /** + * Sets the data to be inserted. + * @param clip The data to insert. + * @return this builder + */ + @NonNull + public Builder setClip(@NonNull ClipData clip) { + mClip = clip; + return this; + } + + /** + * Sets the source of the operation. + * @param source The source of the operation. See {@code SOURCE_} constants. + * @return this builder + */ + @NonNull + public Builder setSource(@Source int source) { + mSource = source; + return this; + } + + /** + * Sets flags that control content insertion behavior. + * @param flags Optional flags to configure the insertion behavior. Use 0 for default + * behavior. See {@code FLAG_} constants. + * @return this builder + */ + @NonNull + public Builder setFlags(@Flags int flags) { + mFlags = flags; + return this; + } + + /** + * Sets the http/https URI for the content. See + * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. + * @param linkUri Optional http/https URI for the content. + * @return this builder + */ + @NonNull + public Builder setLinkUri(@Nullable Uri linkUri) { + mLinkUri = linkUri; + return this; + } + + /** + * Sets additional metadata. + * @param extras Optional bundle with additional metadata. + * @return this builder + */ + @NonNull + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** + * @return A new {@link ContentInfo} instance with the data from this builder. + */ + @NonNull + public ContentInfo build() { + return new ContentInfo(this); + } + } +} diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java index defa58e10e6e..c8bfd36b2b48 100644 --- a/core/java/android/view/Gravity.java +++ b/core/java/android/view/Gravity.java @@ -15,8 +15,13 @@ */ package android.view; + +import android.annotation.IntDef; import android.graphics.Rect; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Standard constants and tools for placing an object within a potentially * larger container. @@ -122,6 +127,32 @@ public class Gravity */ public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END; + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + Gravity.FILL, + Gravity.FILL_HORIZONTAL, + Gravity.FILL_VERTICAL, + Gravity.START, + Gravity.END, + Gravity.LEFT, + Gravity.RIGHT, + Gravity.TOP, + Gravity.BOTTOM, + Gravity.CENTER, + Gravity.CENTER_HORIZONTAL, + Gravity.CENTER_VERTICAL, + Gravity.DISPLAY_CLIP_HORIZONTAL, + Gravity.DISPLAY_CLIP_VERTICAL, + Gravity.CLIP_HORIZONTAL, + Gravity.CLIP_VERTICAL, + Gravity.NO_GRAVITY + }) + public @interface GravityFlags {} + /** * Apply a gravity constant to an object. This supposes that the layout direction is LTR. * diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 1a6eea554cae..a23b7e193024 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -32,6 +32,7 @@ import android.graphics.Region; import android.os.Bundle; import android.os.IRemoteCallback; import android.os.ParcelFileDescriptor; +import android.service.attestation.ImpressionToken; import android.view.DisplayCutout; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; @@ -760,4 +761,23 @@ interface IWindowManager * {@link android.content.pm.PackageManager#getHoldLockToken()}. */ void holdLock(in IBinder token, in int durationMs); + + /** + * Gets an array of support hashing algorithms that can be used to generate the hash of the + * screenshot. The String value of one algorithm should be used when requesting to generate + * the impression attestation token. + * + * @return a String array of supported hashing algorithms. + */ + String[] getSupportedImpressionAlgorithms(); + + /** + * Validate the impression token was generated by the system. The impression token passed in + * should be the token generated when calling {@link IWindowSession#generateImpressionToken} + * + * @param impressionToken The token to verify that it was generated by the system. + * @return true if the token was generated by the system or false if the token cannot be + * verified. + */ + boolean verifyImpressionToken(in ImpressionToken impressionToken); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 0089a852a893..cfdaf8ccc5fb 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -22,6 +22,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; import android.os.Bundle; +import android.service.attestation.ImpressionToken; import android.util.MergedConfiguration; import android.view.DisplayCutout; import android.view.InputChannel; @@ -344,4 +345,16 @@ interface IWindowSession { * window, the system will try to find a new focus target. */ void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus); + + /** + * Generates an impression token that can be used to validate whether specific content was on + * screen. + * + * @param window The token for the window where the view to attest is shown. + * @param boundsInWindow The size and position of the ads view in the window + * @param hashAlgorithm The String for the hashing algorithm to use based on values returned + * from {@link IWindowManager#getSupportedImpressionAlgorithms()} + */ + ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow, + in String hashAlgorithm); } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 1c82619a61ad..c4f32c433598 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -829,7 +829,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#show", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.showRequestFromApiToImeReady", 0); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.showRequestFromIme", 0); } else { @@ -886,7 +886,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation void hide(@InsetsType int types, boolean fromIme) { if (fromIme) { ImeTracing.getInstance().triggerClientDump("InsetsController#hide", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromIme", 0); } else { Trace.asyncTraceBegin(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0); @@ -928,7 +928,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (fromIme) { ImeTracing.getInstance().triggerClientDump( "InsetsController#controlWindowInsetsAnimation", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs, @@ -1022,7 +1022,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation animationType, mHost.getTranslator()); if ((typesReady & WindowInsets.Type.ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsAnimationControlImpl", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } mRunningAnimations.add(new RunningAnimation(runner, animationType)); if (DEBUG) Log.d(TAG, "Animation added to runner. useInsetsAnimationThread: " @@ -1200,7 +1200,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (types.valueAt(j) == ITYPE_IME) { ImeTracing.getInstance().triggerClientDump( "InsetsSourceConsumer#notifyAnimationFinished", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } @@ -1345,7 +1345,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#hideDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -1361,7 +1361,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private void showDirectly(@InsetsType int types, boolean fromIme) { if ((types & ime()) != 0) { ImeTracing.getInstance().triggerClientDump("InsetsController#showDirectly", - mHost.getInputMethodManager()); + mHost.getInputMethodManager(), null /* icProto */); } final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java index db9c53863c7f..b551fa80b5cd 100644 --- a/core/java/android/view/OnReceiveContentListener.java +++ b/core/java/android/view/OnReceiveContentListener.java @@ -16,22 +16,8 @@ package android.view; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ClipData; -import android.net.Uri; -import android.os.Bundle; -import android.util.ArrayMap; - -import com.android.internal.util.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Map; -import java.util.Objects; -import java.util.function.Predicate; /** * Listener for apps to implement handling for insertion of content. Content may be both text and @@ -48,10 +34,13 @@ import java.util.function.Predicate; * public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"}; * * @Override - * public Payload onReceiveContent(View view, Payload payload) { - * Map<Boolean, Payload> split = payload.partition(item -> item.getUri() != null); - * if (split.get(true) != null) { - * ClipData clip = payload.getClip(); + * public ContentInfo onReceiveContent(View view, ContentInfo payload) { + * Map<Boolean, ContentInfo> split = + * payload.partition(item -> item.getUri() != null); + * ContentInfo uriItems = split.get(true); + * ContentInfo remainingItems = split.get(false); + * if (uriItems != null) { + * ClipData clip = uriItems.getClip(); * for (int i = 0; i < clip.getItemCount(); i++) { * Uri uri = clip.getItemAt(i).getUri(); * // ... app-specific logic to handle the URI ... @@ -59,7 +48,7 @@ import java.util.function.Predicate; * } * // Return anything that we didn't handle ourselves. This preserves the default platform * // behavior for text and anything else for which we are not implementing custom handling. - * return split.get(false); + * return remainingItems; * } * } * @@ -83,8 +72,8 @@ public interface OnReceiveContentListener { * handling. For example, an implementation may provide handling for content URIs (to provide * support for inserting images, etc) and delegate the processing of text to the platform to * preserve the common behavior for inserting text. See the class javadoc for a sample - * implementation and see {@link Payload#partition} for a convenient way to split the passed-in - * content. + * implementation and see {@link ContentInfo#partition} for a convenient way to split the + * passed-in content. * * <p>If implementing handling for text: if the view has a selection, the selection should * be overwritten by the passed-in content; if there's no selection, the passed-in content @@ -103,314 +92,6 @@ public interface OnReceiveContentListener { * succeed even if this method returns null. For example, an app may end up not inserting * an item if it exceeds the app's size limit for that type of content. */ - @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload); - - /** - * Holds all the relevant data for a request to {@link OnReceiveContentListener}. - */ - final class Payload { - - /** - * Specifies the UI through which content is being inserted. Future versions of Android may - * support additional values. - * - * @hide - */ - @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, - SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Source {} - - /** - * Specifies that the operation was triggered by the app that contains the target view. - */ - public static final int SOURCE_APP = 0; - - /** - * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or - * "Paste as plain text" action in the insertion/selection menu). - */ - public static final int SOURCE_CLIPBOARD = 1; - - /** - * Specifies that the operation was triggered from the soft keyboard (also known as input - * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard - * for more info. - */ - public static final int SOURCE_INPUT_METHOD = 2; - - /** - * Specifies that the operation was triggered by the drag/drop framework. See - * https://developer.android.com/guide/topics/ui/drag-drop for more info. - */ - public static final int SOURCE_DRAG_AND_DROP = 3; - - /** - * Specifies that the operation was triggered by the autofill framework. See - * https://developer.android.com/guide/topics/text/autofill for more info. - */ - public static final int SOURCE_AUTOFILL = 4; - - /** - * Specifies that the operation was triggered by a result from a - * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection - * menu. - */ - public static final int SOURCE_PROCESS_TEXT = 5; - - /** - * Returns the symbolic name of the given source. - * - * @hide - */ - static String sourceToString(@Source int source) { - switch (source) { - case SOURCE_APP: return "SOURCE_APP"; - case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; - case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; - case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; - case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL"; - case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT"; - } - return String.valueOf(source); - } - - /** - * Flags to configure the insertion behavior. - * - * @hide - */ - @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT}) - @Retention(RetentionPolicy.SOURCE) - public @interface Flags {} - - /** - * Flag requesting that the content should be converted to plain text prior to inserting. - */ - public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0; - - /** - * Returns the symbolic names of the set flags or {@code "0"} if no flags are set. - * - * @hide - */ - static String flagsToString(@Flags int flags) { - if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) { - return "FLAG_CONVERT_TO_PLAIN_TEXT"; - } - return String.valueOf(flags); - } - - @NonNull private final ClipData mClip; - private final @Source int mSource; - private final @Flags int mFlags; - @Nullable private final Uri mLinkUri; - @Nullable private final Bundle mExtras; - - private Payload(Builder b) { - this.mClip = Objects.requireNonNull(b.mClip); - this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT, - "source"); - this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT); - this.mLinkUri = b.mLinkUri; - this.mExtras = b.mExtras; - } - - @NonNull - @Override - public String toString() { - return "Payload{" - + "clip=" + mClip - + ", source=" + sourceToString(mSource) - + ", flags=" + flagsToString(mFlags) - + ", linkUri=" + mLinkUri - + ", extras=" + mExtras - + "}"; - } - - /** - * The data to be inserted. - */ - public @NonNull ClipData getClip() { - return mClip; - } - - /** - * The source of the operation. See {@code SOURCE_} constants. Future versions of Android - * may pass additional values. - */ - public @Source int getSource() { - return mSource; - } - - /** - * Optional flags that control the insertion behavior. See {@code FLAG_} constants. - */ - public @Flags int getFlags() { - return mFlags; - } - - /** - * Optional http/https URI for the content that may be provided by the IME. This is only - * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty - * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the - * IME. - */ - public @Nullable Uri getLinkUri() { - return mLinkUri; - } - - /** - * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will - * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by - * the IME. - */ - public @Nullable Bundle getExtras() { - return mExtras; - } - - /** - * Partitions this payload based on the given predicate. - * - * <p>Similar to a - * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector}, - * this function classifies the content in this payload and organizes it into a map, - * grouping the content that matched vs didn't match the predicate. - * - * <p>Except for the {@link ClipData} items, the returned payloads will contain all the same - * metadata as the original payload. - * - * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which - * partition to place it into. - * @return A map containing the partitioned content. The map will contain a single entry if - * all items were classified into the same partition (all matched or all didn't match the - * predicate) or two entries (if there's at least one item that matched the predicate and at - * least one item that didn't match the predicate). - */ - public @NonNull Map<Boolean, Payload> partition( - @NonNull Predicate<ClipData.Item> itemPredicate) { - if (mClip.getItemCount() == 1) { - Map<Boolean, Payload> result = new ArrayMap<>(1); - result.put(itemPredicate.test(mClip.getItemAt(0)), this); - return result; - } - ArrayList<ClipData.Item> accepted = new ArrayList<>(); - ArrayList<ClipData.Item> remaining = new ArrayList<>(); - for (int i = 0; i < mClip.getItemCount(); i++) { - ClipData.Item item = mClip.getItemAt(i); - if (itemPredicate.test(item)) { - accepted.add(item); - } else { - remaining.add(item); - } - } - Map<Boolean, Payload> result = new ArrayMap<>(2); - if (!accepted.isEmpty()) { - ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted); - result.put(true, new Builder(this).setClip(acceptedClip).build()); - } - if (!remaining.isEmpty()) { - ClipData remainingClip = new ClipData(mClip.getDescription(), remaining); - result.put(false, new Builder(this).setClip(remainingClip).build()); - } - return result; - } - - /** - * Builder for {@link Payload}. - */ - public static final class Builder { - @NonNull private ClipData mClip; - private @Source int mSource; - private @Flags int mFlags; - @Nullable private Uri mLinkUri; - @Nullable private Bundle mExtras; - - /** - * Creates a new builder initialized with the data from the given builder. - */ - public Builder(@NonNull Payload payload) { - mClip = payload.mClip; - mSource = payload.mSource; - mFlags = payload.mFlags; - mLinkUri = payload.mLinkUri; - mExtras = payload.mExtras; - } - - /** - * Creates a new builder. - * @param clip The data to insert. - * @param source The source of the operation. See {@code SOURCE_} constants. - */ - public Builder(@NonNull ClipData clip, @Source int source) { - mClip = clip; - mSource = source; - } - - /** - * Sets the data to be inserted. - * @param clip The data to insert. - * @return this builder - */ - @NonNull - public Builder setClip(@NonNull ClipData clip) { - mClip = clip; - return this; - } - - /** - * Sets the source of the operation. - * @param source The source of the operation. See {@code SOURCE_} constants. - * @return this builder - */ - @NonNull - public Builder setSource(@Source int source) { - mSource = source; - return this; - } - - /** - * Sets flags that control content insertion behavior. - * @param flags Optional flags to configure the insertion behavior. Use 0 for default - * behavior. See {@code FLAG_} constants. - * @return this builder - */ - @NonNull - public Builder setFlags(@Flags int flags) { - mFlags = flags; - return this; - } - - /** - * Sets the http/https URI for the content. See - * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info. - * @param linkUri Optional http/https URI for the content. - * @return this builder - */ - @NonNull - public Builder setLinkUri(@Nullable Uri linkUri) { - mLinkUri = linkUri; - return this; - } - - /** - * Sets additional metadata. - * @param extras Optional bundle with additional metadata. - * @return this builder - */ - @NonNull - public Builder setExtras(@Nullable Bundle extras) { - mExtras = extras; - return this; - } - - /** - * @return A new {@link Payload} instance with the data from this builder. - */ - @NonNull - public Payload build() { - return new Payload(this); - } - } - } + @Nullable + ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload); } diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING index 4ea4310b25a4..c8b746f2248f 100644 --- a/core/java/android/view/TEST_MAPPING +++ b/core/java/android/view/TEST_MAPPING @@ -2,6 +2,24 @@ "presubmit": [ { "name": "CtsAccelerationTestCases" + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "include-filter": "android.os.cts.StrictModeTest" + } + ], + "file_patterns": ["(/|^)ViewConfiguration.java", "(/|^)GestureDetector.java"] } ], "imports": [ diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 30ec2b050dbe..a5c66537b11f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -112,7 +112,6 @@ import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.ContextMenu.ContextMenuInfo; import android.view.InputDevice.InputSourceClass; -import android.view.OnReceiveContentListener.Payload; import android.view.Window.OnContentApplyWindowInsetsListener; import android.view.WindowInsets.Type; import android.view.WindowInsetsAnimation.Bounds; @@ -9063,11 +9062,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The portion of the passed-in content that was not accepted (may be all, some, or none * of the passed-in content). */ - public @Nullable Payload performReceiveContent(@NonNull Payload payload) { + @Nullable + public ContentInfo performReceiveContent(@NonNull ContentInfo payload) { final OnReceiveContentListener listener = (mListenerInfo == null) ? null : getListenerInfo().mOnReceiveContentListener; if (listener != null) { - final Payload remaining = listener.onReceiveContent(this, payload); + final ContentInfo remaining = listener.onReceiveContent(this, payload); return (remaining == null) ? null : onReceiveContent(remaining); } return onReceiveContent(payload); @@ -9088,7 +9088,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The portion of the passed-in content that was not handled (may be all, some, or none * of the passed-in content). */ - public @Nullable Payload onReceiveContent(@NonNull Payload payload) { + @Nullable + public ContentInfo onReceiveContent(@NonNull ContentInfo payload) { return payload; } @@ -9113,7 +9114,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The MIME types accepted by {@link #performReceiveContent} for this view (may * include patterns such as "image/*"). */ - public @Nullable String[] getOnReceiveContentMimeTypes() { + @Nullable + public String[] getOnReceiveContentMimeTypes() { return mOnReceiveContentMimeTypes; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2bea0d6b4b04..8321f2b28771 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -9225,7 +9225,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#showInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.showInsets(types, fromIme); @@ -9238,7 +9239,8 @@ public final class ViewRootImpl implements ViewParent, final ViewRootImpl viewAncestor = mViewAncestor.get(); if (fromIme) { ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#hideInsets", - viewAncestor.getInsetsController().getHost().getInputMethodManager()); + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); } if (viewAncestor != null) { viewAncestor.hideInsets(types, fromIme); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8c8ea00ef41f..b9afbc95de0d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -104,6 +104,7 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.util.proto.ProtoOutputStream; +import android.view.Gravity.GravityFlags; import android.view.View.OnApplyWindowInsetsListener; import android.view.WindowInsets.Side; import android.view.WindowInsets.Side.InsetsSide; @@ -2157,15 +2158,6 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000; /** - * Flag to indicate that this window should be considered a screen decoration similar to the - * nav bar and status bar. This will cause this window to affect the window insets reported - * to other windows when it is visible. - * @hide - */ - @RequiresPermission(permission.STATUS_BAR_SERVICE) - public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000; - - /** * Flag to indicate that the status bar window is in a state such that it forces showing * the navigation bar unless the navigation bar window is explicitly set to * {@link View#GONE}. @@ -2270,7 +2262,6 @@ public interface WindowManager extends ViewManager { PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC, PRIVATE_FLAG_USE_BLAST, @@ -2358,10 +2349,6 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY, name = "IS_ROUNDED_CORNERS_OVERLAY"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_IS_SCREEN_DECOR, - equals = PRIVATE_FLAG_IS_SCREEN_DECOR, - name = "IS_SCREEN_DECOR"), - @ViewDebug.FlagToString( mask = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, equals = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION, name = "STATUS_FORCE_SHOW_NAVIGATION"), @@ -2586,6 +2573,7 @@ public interface WindowManager extends ViewManager { * * @see Gravity */ + @GravityFlags public int gravity; /** diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 0ed7ca79113e..673073ea197d 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -24,6 +24,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.os.RemoteException; +import android.service.attestation.ImpressionToken; import android.util.Log; import android.util.MergedConfiguration; import android.window.ClientWindowFrames; @@ -460,4 +461,10 @@ public class WindowlessWindowManager implements IWindowSession { public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken, boolean grantFocus) { } + + @Override + public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow, + String hashAlgorithm) { + return null; + } } diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 8fdcac7b4f9d..364ae8186e54 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -19,7 +19,7 @@ package android.view.autofill; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_AUTOFILL; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; import static android.view.autofill.Helper.toList; @@ -61,8 +61,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.Choreographer; +import android.view.ContentInfo; import android.view.KeyEvent; -import android.view.OnReceiveContentListener.Payload; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -2371,8 +2371,8 @@ public final class AutofillManager { reportAutofillContentFailure(id); return; } - Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); - Payload result = view.performReceiveContent(payload); + ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo result = view.performReceiveContent(payload); if (result != null) { Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id + ", view=" + view + ", clip=" + clip); diff --git a/core/java/android/view/autofill/TEST_MAPPING b/core/java/android/view/autofill/TEST_MAPPING new file mode 100644 index 000000000000..87a17ebee36d --- /dev/null +++ b/core/java/android/view/autofill/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/services/autofill/java/com/android/server/autofill" + } + ] +} diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 1ab9edf0caa2..f057c1239e52 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -16,7 +16,7 @@ package android.view.inputmethod; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; import android.annotation.CallSuper; import android.annotation.IntRange; @@ -38,9 +38,9 @@ import android.text.TextUtils; import android.text.method.MetaKeyKeyListener; import android.util.Log; import android.util.LogPrinter; +import android.view.ContentInfo; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.OnReceiveContentListener; import android.view.View; import com.android.internal.util.Preconditions; @@ -952,8 +952,8 @@ public class BaseInputConnection implements InputConnection { } final ClipData clip = new ClipData(inputContentInfo.getDescription(), new ClipData.Item(inputContentInfo.getContentUri())); - final OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD) + final ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD) .setLinkUri(inputContentInfo.getLinkUri()) .setExtras(opts) .build(); diff --git a/core/java/android/view/inputmethod/DumpableInputConnection.java b/core/java/android/view/inputmethod/DumpableInputConnection.java new file mode 100644 index 000000000000..9819a5734491 --- /dev/null +++ b/core/java/android/view/inputmethod/DumpableInputConnection.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.inputmethod; + +import android.annotation.NonNull; +import android.util.proto.ProtoOutputStream; + +/** @hide */ +public interface DumpableInputConnection { + + /** + * Method used to dump state of InputConnection implementations of interest. + * + * @param proto Stream to write the state to + * @param fieldId FieldId of DumpableInputConnection as defined in the parent message + */ + void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 315d4461692c..907b5b085b59 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -22,6 +22,8 @@ import static android.util.imetracing.ImeTracing.PROTO_ARG; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER; +import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION; +import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION_CALL; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER; import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL; import static android.view.inputmethod.InputMethodManagerProto.ACTIVE; @@ -87,8 +89,10 @@ import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.autofill.AutofillManager; import com.android.internal.annotations.GuardedBy; +import com.android.internal.inputmethod.Completable; import com.android.internal.inputmethod.InputMethodDebug; import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; +import com.android.internal.inputmethod.ResultCallbacks; import com.android.internal.inputmethod.StartInputFlags; import com.android.internal.inputmethod.StartInputReason; import com.android.internal.inputmethod.UnbindReason; @@ -576,7 +580,8 @@ public final class InputMethodManager { int windowFlags) { final View servedView; ImeTracing.getInstance().triggerClientDump( - "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this); + "InputMethodManager.DelegateImpl#startInput", InputMethodManager.this, + null /* icProto */); synchronized (mH) { mCurrentTextBoxAttribute = null; mCompletions = null; @@ -665,6 +670,7 @@ public final class InputMethodManager { final int startInputReason = nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; + final Completable.InputBindResult value = Completable.createInputBindResult(); mService.startInputOrWindowGainedFocus( startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, @@ -672,7 +678,9 @@ public final class InputMethodManager { null, null, 0 /* missingMethodFlags */, - mCurRootView.mContext.getApplicationInfo().targetSdkVersion); + mCurRootView.mContext.getApplicationInfo().targetSdkVersion, + ResultCallbacks.of(value)); + Completable.getResult(value); // ignore the result } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -998,9 +1006,9 @@ public final class InputMethodManager { private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; - ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, + ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn, InputMethodManager inputMethodManager, View servedView) { - super(mainLooper, conn); + super(icLooper, conn); mParentInputMethodManager = inputMethodManager; mServedView = new WeakReference<>(servedView); } @@ -1010,6 +1018,11 @@ public final class InputMethodManager { return mParentInputMethodManager.mActive && !isFinished(); } + @Override + public InputMethodManager getIMM() { + return mParentInputMethodManager; + } + void deactivate() { if (isFinished()) { // This is a small performance optimization. Still only the 1st call of @@ -1046,6 +1059,18 @@ public final class InputMethodManager { + " mServedView=" + mServedView.get() + "}"; } + + void dumpDebug(ProtoOutputStream proto, long fieldId) { + // Check that the call is initiated in the main thread of the current InputConnection + // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are + // executed on this thread. Otherwise the messages are dispatched to the correct thread + // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance + // reasons. + if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper() + == getLooper()) { + ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId); + } + } } private static class ImeThreadFactory implements ThreadFactory { @@ -1695,7 +1720,8 @@ public final class InputMethodManager { * {@link #RESULT_HIDDEN}. */ public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) { - ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this); + ImeTracing.getInstance().triggerClientDump("InputMethodManager#showSoftInput", this, + null /* icProto */); // Re-dispatch if there is a context mismatch. final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view); if (fallbackImm != null) { @@ -1804,7 +1830,7 @@ public final class InputMethodManager { public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver) { ImeTracing.getInstance().triggerClientDump("InputMethodManager#hideSoftInputFromWindow", - this); + this, null /* icProto */); checkFocus(); synchronized (mH) { final View servedView = getServedViewLocked(); @@ -2026,10 +2052,13 @@ public final class InputMethodManager { if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic=" + ic + " tba=" + tba + " startInputFlags=" + InputMethodDebug.startInputFlagsToString(startInputFlags)); - res = mService.startInputOrWindowGainedFocus( + final Completable.InputBindResult value = Completable.createInputBindResult(); + mService.startInputOrWindowGainedFocus( startInputReason, mClient, windowGainingFocus, startInputFlags, softInputMode, windowFlags, tba, servedContext, missingMethodFlags, - view.getContext().getApplicationInfo().targetSdkVersion); + view.getContext().getApplicationInfo().targetSdkVersion, + ResultCallbacks.of(value)); + res = Completable.getResult(value); if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res); if (res == null) { Log.wtf(TAG, "startInputOrWindowGainedFocus must not return" @@ -2207,6 +2236,8 @@ public final class InputMethodManager { * @hide */ public void notifyImeHidden(IBinder windowToken) { + ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this, + null /* icProto */); synchronized (mH) { try { if (mCurMethod != null && mCurRootView != null @@ -3274,7 +3305,7 @@ public final class InputMethodManager { for (String arg : args) { if (arg.equals(PROTO_ARG)) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - dumpDebug(proto); + dumpDebug(proto, null /* icProto */); proto.flush(); return true; } @@ -3287,10 +3318,11 @@ public final class InputMethodManager { * {@link ProtoOutputStream}. * * @param proto The proto stream to which the dumps are written. + * @param icProto {@link InputConnection} call data in proto format. * @hide */ @GuardedBy("mH") - public void dumpDebug(ProtoOutputStream proto) { + public void dumpDebug(ProtoOutputStream proto, ProtoOutputStream icProto) { if (mCurMethod == null) { return; } @@ -3312,6 +3344,12 @@ public final class InputMethodManager { if (mImeInsetsConsumer != null) { mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER); } + if (mServedInputConnectionWrapper != null) { + mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION); + } + if (icProto != null) { + proto.write(INPUT_CONNECTION_CALL, icProto.getBytes()); + } } } } diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 14abbdb6d33f..9c63d56d74da 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -71,7 +71,8 @@ public final class InputMethodSubtype implements Parcelable { // TODO: remove this private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME = "UntranslatableReplacementStringInSubtypeName"; - private static final int SUBTYPE_ID_NONE = 0; + /** {@hide} */ + public static final int SUBTYPE_ID_NONE = 0; private final boolean mIsAuxiliary; private final boolean mOverridesImplicitlyEnabledSubtype; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index dde9c3089370..b91e7d39f51c 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -251,15 +251,8 @@ public final class WebViewFactory { Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()"); try { Class<WebViewFactoryProvider> providerClass = getProviderClass(); - Method staticFactory = null; - try { - staticFactory = providerClass.getMethod( + Method staticFactory = providerClass.getMethod( CHROMIUM_WEBVIEW_FACTORY_METHOD, WebViewDelegate.class); - } catch (Exception e) { - if (DEBUG) { - Log.w(LOGTAG, "error instantiating provider with static factory method", e); - } - } Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactoryProvider invocation"); try { @@ -267,12 +260,12 @@ public final class WebViewFactory { staticFactory.invoke(null, new WebViewDelegate()); if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance); return sProviderInstance; - } catch (Exception e) { - Log.e(LOGTAG, "error instantiating provider", e); - throw new AndroidRuntimeException(e); } finally { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } + } catch (Exception e) { + Log.e(LOGTAG, "error instantiating provider", e); + throw new AndroidRuntimeException(e); } finally { Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 00ba326d2ba9..0025d1e2a853 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -16,7 +16,7 @@ package android.widget; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; import android.R; import android.animation.ValueAnimator; @@ -87,6 +87,7 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; import android.view.ActionMode.Callback; +import android.view.ContentInfo; import android.view.ContextMenu; import android.view.ContextThemeWrapper; import android.view.DragAndDropPermissions; @@ -98,7 +99,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import android.view.OnReceiveContentListener; import android.view.SubMenu; import android.view.View; import android.view.View.DragShadowBuilder; @@ -2869,8 +2869,8 @@ public class Editor { final int originalLength = mTextView.getText().length(); Selection.setSelection((Spannable) mTextView.getText(), offset); final ClipData clip = event.getClipData(); - final OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP) + final ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP) .build(); mTextView.performReceiveContent(payload); if (dragDropIntoItself) { diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 7c20472df357..4f1c40a8d1c2 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -2773,6 +2773,7 @@ public class NumberPicker extends LinearLayout { int left, int top, int right, int bottom) { AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo(); info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT); + info.setAccessibilityFocused(mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT); if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) { info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); } @@ -2802,6 +2803,7 @@ public class NumberPicker extends LinearLayout { info.setClickable(true); info.setLongClickable(true); info.setEnabled(NumberPicker.this.isEnabled()); + info.setAccessibilityFocused(mAccessibilityFocusedView == virtualViewId); Rect boundsInParent = mTempRect; boundsInParent.set(left, top, right, bottom); info.setVisibleToUser(isVisibleToUser(boundsInParent)); @@ -2843,6 +2845,7 @@ public class NumberPicker extends LinearLayout { info.setParent((View) getParentForAccessibility()); info.setEnabled(NumberPicker.this.isEnabled()); info.setScrollable(true); + info.setAccessibilityFocused(mAccessibilityFocusedView == View.NO_ID); final float applicationScale = getContext().getResources().getCompatibilityInfo().applicationScale; diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index a034a7c2dc7e..e08ccfddc4c5 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -156,8 +156,7 @@ public final class SelectionActionModeHelper { mSmartSelectSprite != null ? this::startSelectionActionModeWithSmartSelectAnimation : this::startSelectionActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -179,8 +178,7 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::startLinkActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -196,8 +194,7 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::invalidateActionMode, - mTextClassificationHelper::getOriginalSelection, - mTextClassificationHelper::isTextClassifierDestroyed) + mTextClassificationHelper::getOriginalSelection) .execute(); } } @@ -995,7 +992,6 @@ public final class SelectionActionModeHelper { private final Supplier<SelectionResult> mSelectionResultSupplier; private final Consumer<SelectionResult> mSelectionResultCallback; private final Supplier<SelectionResult> mTimeOutResultSupplier; - private final Supplier<Boolean> mIsTextClassifierDestroyedSupplier; private final TextView mTextView; private final String mOriginalText; @@ -1010,16 +1006,13 @@ public final class SelectionActionModeHelper { @NonNull TextView textView, int timeOut, @NonNull Supplier<SelectionResult> selectionResultSupplier, @NonNull Consumer<SelectionResult> selectionResultCallback, - @NonNull Supplier<SelectionResult> timeOutResultSupplier, - @NonNull Supplier<Boolean> isTextClassifierDestroyedSupplier) { + @NonNull Supplier<SelectionResult> timeOutResultSupplier) { super(textView != null ? textView.getHandler() : null); mTextView = Objects.requireNonNull(textView); mTimeOutDuration = timeOut; mSelectionResultSupplier = Objects.requireNonNull(selectionResultSupplier); mSelectionResultCallback = Objects.requireNonNull(selectionResultCallback); mTimeOutResultSupplier = Objects.requireNonNull(timeOutResultSupplier); - mIsTextClassifierDestroyedSupplier = - Objects.requireNonNull(isTextClassifierDestroyedSupplier); // Make a copy of the original text. mOriginalText = getText(mTextView).toString(); } @@ -1033,14 +1026,8 @@ public final class SelectionActionModeHelper { try { result = mSelectionResultSupplier.get(); } catch (IllegalStateException e) { - // Swallows the exception if the text classifier session is destroyed - if (mIsTextClassifierDestroyedSupplier.get()) { - Log.w(LOG_TAG, - "TextClassificationAsyncTask failed because TextClassifier destroyed", - e); - } else { - throw e; - } + // TODO(b/174300371): Only swallows the exception if the TCSession is destroyed + Log.w(LOG_TAG, "TextClassificationAsyncTask failed.", e); } mTextView.removeCallbacks(onTimeOut); return result; @@ -1173,10 +1160,6 @@ public final class SelectionActionModeHelper { } } - public boolean isTextClassifierDestroyed() { - return mTextClassifier.get().isDestroyed(); - } - private boolean isDarkLaunchEnabled() { return TextClassificationManager.getSettings(mContext).isModelDarkLaunchEnabled(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 98f808784803..02a930017906 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,10 +17,10 @@ package android.widget; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; -import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT; +import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_CLIPBOARD; +import static android.view.ContentInfo.SOURCE_PROCESS_TEXT; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH; import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX; @@ -146,6 +146,7 @@ import android.util.TypedValue; import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.ActionMode; import android.view.Choreographer; +import android.view.ContentInfo; import android.view.ContextMenu; import android.view.DragEvent; import android.view.Gravity; @@ -154,7 +155,6 @@ import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.OnReceiveContentListener.Payload; import android.view.PointerIcon; import android.view.View; import android.view.ViewConfiguration; @@ -964,6 +964,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ public static void preloadFontCache() { + if (Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + return; + } Paint p = new Paint(); p.setAntiAlias(true); // Ensure that the Typeface is loaded here. @@ -2151,7 +2154,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (result != null) { if (isTextEditable()) { ClipData clip = ClipData.newPlainText("", result); - Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build(); performReceiveContent(payload); if (mEditor != null) { mEditor.refreshTextActionMode(); @@ -11857,7 +11861,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " cannot be autofilled into " + this); return; } - final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build(); + final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); performReceiveContent(payload); } @@ -12924,7 +12928,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (clip == null) { return; } - final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD) + final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_CLIPBOARD) .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT) .build(); performReceiveContent(payload); @@ -13740,8 +13744,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @return The portion of the passed-in content that was not handled (may be all, some, or none * of the passed-in content). */ + @Nullable @Override - public @Nullable Payload onReceiveContent(@NonNull Payload payload) { + public ContentInfo onReceiveContent(@NonNull ContentInfo payload) { if (mEditor != null) { return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload); } diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java index 7ef68ec7a4ee..8cef1061c423 100644 --- a/core/java/android/widget/TextViewOnReceiveContentListener.java +++ b/core/java/android/widget/TextViewOnReceiveContentListener.java @@ -17,10 +17,10 @@ package android.widget; import static android.content.ContentResolver.SCHEME_CONTENT; -import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; import android.annotation.NonNull; import android.annotation.Nullable; @@ -38,9 +38,10 @@ import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.util.Log; +import android.view.ContentInfo; +import android.view.ContentInfo.Flags; +import android.view.ContentInfo.Source; import android.view.OnReceiveContentListener; -import android.view.OnReceiveContentListener.Payload.Flags; -import android.view.OnReceiveContentListener.Payload.Source; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -53,20 +54,21 @@ import java.util.ArrayList; import java.util.Arrays; /** - * Default implementation of {@link OnReceiveContentListener} for editable - * {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML, - * etc) but not images or other content. + * Default implementation for {@link View#onReceiveContent} for editable {@link TextView} + * components. This class handles insertion of text (plain text, styled text, HTML, etc) but not + * images or other content. * * @hide */ @VisibleForTesting public final class TextViewOnReceiveContentListener implements OnReceiveContentListener { - private static final String LOG_TAG = "OnReceiveContent"; + private static final String LOG_TAG = "ReceiveContent"; @Nullable private InputConnectionInfo mInputConnectionInfo; + @Nullable @Override - public @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload) { + public ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload) { if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { Log.d(LOG_TAG, "onReceive: " + payload); } @@ -126,7 +128,7 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL editable.replace(start, end, replacement); } - private void onReceiveForAutofill(@NonNull TextView view, @NonNull Payload payload) { + private void onReceiveForAutofill(@NonNull TextView view, @NonNull ContentInfo payload) { ClipData clip = payload.getClip(); if (isUsageOfImeCommitContentEnabled(view)) { clip = handleNonTextViaImeCommitContent(clip); @@ -145,7 +147,8 @@ public final class TextViewOnReceiveContentListener implements OnReceiveContentL Selection.setSelection(editable, editable.length()); } - private static void onReceiveForDragAndDrop(@NonNull TextView view, @NonNull Payload payload) { + private static void onReceiveForDragAndDrop(@NonNull TextView view, + @NonNull ContentInfo payload) { final CharSequence text = coerceToText(payload.getClip(), view.getContext(), payload.getFlags()); replaceSelection((Editable) view.getText(), text); diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java new file mode 100644 index 000000000000..ec6779216ae5 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import android.annotation.AnyThread; +import android.annotation.NonNull; +import android.os.RemoteException; + +import com.android.internal.view.InputBindResult; + +import java.util.function.Supplier; + +/** + * Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}. + */ +public final class CallbackUtils { + + /** + * Not intended to be instantiated. + */ + private CallbackUtils() { + } + + /** + * A utility method using given {@link IInputBindResultResultCallback} to callback the + * {@link InputBindResult}. + * + * @param callback {@link IInputBindResultResultCallback} to be called back. + * @param resultSupplier the supplier from which {@link InputBindResult} is provided. + */ + @AnyThread + public static void onResult(@NonNull IInputBindResultResultCallback callback, + @NonNull Supplier<InputBindResult> resultSupplier) { + try { + callback.onResult(resultSupplier.get()); + } catch (RemoteException ignored) { } + } +} diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java index d8d1a7df6aa8..b9e1cf09dc07 100644 --- a/core/java/com/android/internal/inputmethod/Completable.java +++ b/core/java/com/android/internal/inputmethod/Completable.java @@ -124,6 +124,16 @@ public final class Completable { return true; } } + + /** + * Blocks the calling thread until this object becomes ready to return the value. + */ + @AnyThread + public void await() { + try { + mLatch.await(); + } catch (InterruptedException ignored) { } + } } /** @@ -250,6 +260,13 @@ public final class Completable { } /** + * @return an instance of {@link Completable.InputBindResult}. + */ + public static Completable.InputBindResult createInputBindResult() { + return new Completable.InputBindResult(); + } + + /** * Completable object of {@link java.lang.Boolean}. */ public static final class Boolean extends Values<java.lang.Boolean> { } @@ -278,6 +295,18 @@ public final class Completable { extends Values<com.android.internal.view.InputBindResult> { } /** + * Await the result by the {@link Completable.Values}. + * + * @return the result once {@link ValueBase#onComplete()} + */ + @AnyThread + @Nullable + public static <T> T getResult(@NonNull Completable.Values<T> value) { + value.await(); + return value.getValue(); + } + + /** * Await the result by the {@link Completable.Int}, and log it if there is no result after * given timeout. * diff --git a/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl new file mode 100644 index 000000000000..b52b3b100ed0 --- /dev/null +++ b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.inputmethod; + +import com.android.internal.view.InputBindResult; + +oneway interface IInputBindResultResultCallback { + void onResult(in InputBindResult result); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java index 7131284e42df..c59dcf4ce420 100644 --- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java +++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java @@ -21,6 +21,8 @@ import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.view.InputBindResult; + import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicReference; @@ -154,4 +156,31 @@ public final class ResultCallbacks { } }; } + + /** + * Creates {@link IInputBindResultResultCallback.Stub} that is to set + * {@link Completable.InputBindResult} when receiving the result. + * + * @param value {@link Completable.InputBindResult} to be set when receiving the result. + * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC + * parameter. + */ + @AnyThread + public static IInputBindResultResultCallback.Stub of( + @NonNull Completable.InputBindResult value) { + final AtomicReference<WeakReference<Completable.InputBindResult>> + atomicRef = new AtomicReference<>(new WeakReference<>(value)); + + return new IInputBindResultResultCallback.Stub() { + @BinderThread + @Override + public void onResult(InputBindResult result) { + final Completable.InputBindResult value = unwrap(atomicRef); + if (value == null) { + return; + } + value.onComplete(result); + } + }; + } } diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java index ffc7f05eb703..1553e2eb0793 100644 --- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java +++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java @@ -48,7 +48,8 @@ import java.lang.annotation.Retention; SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED, SoftInputShowHideReason.HIDE_RECENTS_ANIMATION, SoftInputShowHideReason.HIDE_BUBBLES, - SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR}) + SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT}) public @interface SoftInputShowHideReason { /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */ int SHOW_SOFT_INPUT = 0; @@ -161,4 +162,9 @@ public @interface SoftInputShowHideReason { * soft-input when the same window focused again to align with the same behavior prior to R. */ int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20; + + /** + * Hide soft input when a {@link com.android.internal.view.IInputMethodClient} is removed. + */ + int HIDE_REMOVE_CLIENT = 21; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7571f5db8cad..dcd74fd1ad3e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -38,6 +38,7 @@ import android.net.NetworkStats; import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; +import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.Binder; import android.os.Build; @@ -54,6 +55,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.os.WorkSource.WorkChain; @@ -144,6 +146,14 @@ import java.util.concurrent.locks.ReentrantLock; public class BatteryStatsImpl extends BatteryStats { private static final String TAG = "BatteryStatsImpl"; private static final boolean DEBUG = false; + + // TODO(b/169376495): STOPSHIP if true + private static final boolean DEBUG_FOREGROUND_STATS = true; + + private static final boolean ENABLE_FOREGROUND_STATS_COLLECTION = + DEBUG_FOREGROUND_STATS && SystemProperties.getBoolean( + "debug.battery_foreground_stats_collection", false); + public static final boolean DEBUG_ENERGY = false; private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY; private static final boolean DEBUG_BINDER_STATS = false; @@ -739,6 +749,37 @@ public class BatteryStatsImpl extends BatteryStats { long mTrackRunningHistoryElapsedRealtimeMs = 0; long mTrackRunningHistoryUptimeMs = 0; + private static final int FOREGROUND_UID_INITIAL_CAPACITY = 10; + private static final int INVALID_UID = -1; + + private final IntArray mForegroundUids = ENABLE_FOREGROUND_STATS_COLLECTION + ? new IntArray(FOREGROUND_UID_INITIAL_CAPACITY) : null; + + // Last recorded battery energy capacity. + // This is used for computing foregrund power per application. + // See: PowerForUid below + private long mLastBatteryEnergyCapacityNWh = 0; + + private static final class PowerForUid { + public long energyNwh = 0; + // Same as energyNwh, but not tracked for the first 2 minutes; + public long filteredEnergyNwh = 0; + public double totalHours = 0; + public long baseTimeMs = 0; + + double computePower() { + // units in nW + return totalHours != 0 ? energyNwh / totalHours : -1.0; + } + + double computeFilteredPower() { + // units in nW + return totalHours != 0 ? filteredEnergyNwh / totalHours : -1.0; + } + } + private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION + ? new HashMap<>() : null; + final BatteryStatsHistory mBatteryStatsHistory; final HistoryItem mHistoryCur = new HistoryItem(); @@ -1026,6 +1067,8 @@ public class BatteryStatsImpl extends BatteryStats { private int mNumConnectivityChange; + private int mBatteryVolt = -1; + private int mBatteryCharge = -1; private int mEstimatedBatteryCapacity = -1; private int mMinLearnedBatteryCapacity = -1; @@ -4154,6 +4197,49 @@ public class BatteryStatsImpl extends BatteryStats { // TODO(b/155216561): It is possible for isolated uids to be in a higher // state than its parent uid. We should track the highest state within the union of host // and isolated uids rather than only the parent uid. + + + int uidState = mapToInternalProcessState(state); + + boolean isForeground = (uidState == Uid.PROCESS_STATE_TOP) + || (uidState == Uid.PROCESS_STATE_FOREGROUND); + + + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + boolean previouslyInForegrond = false; + for (int i = 0; i < mForegroundUids.size(); i++) { + if (mForegroundUids.get(i) == uid) { + previouslyInForegrond = true; + if (!isForeground) { + // If we were previously in the foreground, remove the uid + // from the foreground set and dirty the slot. + mForegroundUids.set(i, INVALID_UID); + final PowerForUid pfu = + mUidToPower.computeIfAbsent(uid, unused -> new PowerForUid()); + pfu.baseTimeMs = 0; + break; + } + } + } + + if (!previouslyInForegrond && isForeground) { + boolean addedToForeground = false; + // Check if we have a free slot to clobber... + for (int i = 0; i < mForegroundUids.size(); i++) { + if (mForegroundUids.get(i) == INVALID_UID) { + addedToForeground = true; + mForegroundUids.set(i, uid); + break; + } + } + + // ...if not, append to the end of the array. + if (!addedToForeground) { + mForegroundUids.add(uid); + } + } + } + FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid, ActivityManager.processStateAmToProto(state)); getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs) @@ -12968,6 +13054,7 @@ public class BatteryStatsImpl extends BatteryStats { doWrite = true; resetAllStatsLocked(mSecUptime, mSecRealtime); if (chargeUAh > 0 && level > 0) { + mBatteryCharge = chargeUAh; // Only use the reported coulomb charge value if it is supported and reported. mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0)); } @@ -13140,6 +13227,47 @@ public class BatteryStatsImpl extends BatteryStats { startRecordingHistory(elapsedRealtimeMs, uptimeMs, true); } } + + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + mBatteryVolt = volt; + if (onBattery) { + final long energyNwh = (volt * (long) chargeUAh); + final long energyDelta = mLastBatteryEnergyCapacityNWh - energyNwh; + for (int i = 0; i < mForegroundUids.size(); i++) { + final int uid = mForegroundUids.get(i); + if (uid == INVALID_UID) { + continue; + } + final PowerForUid pfu = mUidToPower + .computeIfAbsent(uid, unused -> new PowerForUid()); + if (pfu.baseTimeMs <= 0) { + pfu.baseTimeMs = currentTimeMs; + } else { + // Check if mLastBatteryEnergyCapacityNWh > energyNwh, + // to make sure we only count discharges + if (energyDelta > 0) { + pfu.energyNwh += energyDelta; + // Convert from milliseconds to hours + // 1000 ms per second * 3600 seconds per hour + pfu.totalHours += ((double) (currentTimeMs - pfu.baseTimeMs) + / (1.0 * 1000 * 60 * 60)); + // Now convert from 2 minutes to hours + // 2 minutes = 1/30 of an hour + if (pfu.totalHours > (2.0 / 60)) { + pfu.filteredEnergyNwh += energyDelta; + } + + } + pfu.baseTimeMs = currentTimeMs; + } + } + mLastBatteryEnergyCapacityNWh = energyNwh; + } else if (onBattery != mOnBattery) { + // Transition to onBattery = false + mUidToPower.values().forEach(v -> v.baseTimeMs = 0); + } + } + mCurrentBatteryLevel = level; if (mDischargePlugLevel < 0) { mDischargePlugLevel = level; @@ -16056,6 +16184,48 @@ public class BatteryStatsImpl extends BatteryStats { } public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) { + if (ENABLE_FOREGROUND_STATS_COLLECTION) { + long actualCharge = -1; + long actualEnergy = -1; + try { + IBatteryPropertiesRegistrar registrar = + IBatteryPropertiesRegistrar.Stub.asInterface( + ServiceManager.getService("batteryproperties")); + if (registrar != null) { + BatteryProperty prop = new BatteryProperty(); + if (registrar.getProperty( + BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) { + actualCharge = prop.getLong(); + } + prop = new BatteryProperty(); + if (registrar.getProperty( + BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) { + actualEnergy = prop.getLong(); + } + } + } catch (RemoteException e) { + // Ignore. + } + pw.printf("ActualCharge (uAh): %d\n", (int) actualCharge); + pw.printf("ActualEnergy (nWh): %d\n", actualEnergy); + pw.printf("mBatteryCharge (uAh): %d\n", mBatteryCharge); + pw.printf("mBatteryVolts (mV): %d\n", mBatteryVolt); + pw.printf("est energy (nWh): %d\n", mBatteryVolt * (long) mBatteryCharge); + pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacity); + pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacity); + pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacity); + pw.printf("est. capacity: %f\n", + (float) actualCharge / (mEstimatedBatteryCapacity * 1000)); + pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel); + pw.println("Total Power per app:"); + mUidToPower.entrySet().forEach(e -> + pw.printf("Uid: %d, Total watts (nW): %f\n", + e.getKey(), e.getValue().computePower())); + pw.println("Total Power per app after first 2 minutes initial launch:"); + mUidToPower.entrySet().forEach(e -> + pw.printf("Uid: %d, Total watts (nW): %f\n", + e.getKey(), e.getValue().computeFilteredPower())); + } if (DEBUG) { pw.println("mOnBatteryTimeBase:"); mOnBatteryTimeBase.dump(pw, " "); diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java index 94e07a85867e..929c9e8bb8c1 100644 --- a/core/java/com/android/internal/util/FastXmlSerializer.java +++ b/core/java/com/android/internal/util/FastXmlSerializer.java @@ -367,8 +367,11 @@ public class FastXmlSerializer implements XmlSerializer { public void startDocument(String encoding, Boolean standalone) throws IOException, IllegalArgumentException, IllegalStateException { - append("<?xml version='1.0' encoding='utf-8' standalone='" - + (standalone ? "yes" : "no") + "' ?>\n"); + append("<?xml version='1.0' encoding='utf-8'"); + if (standalone != null) { + append(" standalone='" + (standalone ? "yes" : "no") + "'"); + } + append(" ?>\n"); mLineStart = true; } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 33ebe43cb23a..1d5935db6322 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -25,7 +25,11 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.Trace; import android.util.Log; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -35,6 +39,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionInspector; import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags; import android.view.inputmethod.InputContentInfo; +import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.SurroundingText; import com.android.internal.annotations.GuardedBy; @@ -111,13 +116,21 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } } + protected Looper getLooper() { + synchronized (mMainLooper) { + return mMainLooper; + } + } + protected boolean isFinished() { synchronized (mLock) { return mFinished; } } - abstract protected boolean isActive(); + protected abstract boolean isActive(); + + protected abstract InputMethodManager getIMM(); public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) { dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback)); @@ -257,63 +270,100 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } void executeMessage(Message msg) { + ProtoOutputStream icProto; switch (msg.what) { case DO_GET_TEXT_AFTER_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextAfterCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextAfterCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextAfterCursor(msg.arg1, msg.arg2); + } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextAfterCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextAfterCursor", getIMM(), icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextAfterCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_TEXT_BEFORE_CURSOR: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); - result = null; - } else { - result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getTextBeforeCursor on inactive InputConnection"); + result = null; + } else { + result = ic.getTextBeforeCursor(msg.arg1, msg.arg2); + } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(msg.arg1, + msg.arg2, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getTextBeforeCursor", getIMM(), icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getTextBeforeCursor()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SELECTED_TEXT: { - final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final CharSequence result; - if (ic == null || !isActive()) { - Log.w(TAG, "getSelectedText on inactive InputConnection"); - result = null; - } else { - result = ic.getSelectedText(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getSelectedText()." - + " result=" + result, e); + final ICharSequenceResultCallback callback = + (ICharSequenceResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final CharSequence result; + if (ic == null || !isActive()) { + Log.w(TAG, "getSelectedText on inactive InputConnection"); + result = null; + } else { + result = ic.getSelectedText(msg.arg1); + } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSelectedTextProto(msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSelectedText", getIMM(), icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getSelectedText()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_SURROUNDING_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText"); try { int beforeLength = (int) args.arg1; int afterLength = (int) args.arg2; @@ -328,6 +378,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getSurroundingText(beforeLength, afterLength, flags); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetSurroundingTextProto(beforeLength, + afterLength, flags, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getSurroundingText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -335,30 +391,43 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_GET_CURSOR_CAPS_MODE: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final int result; - if (ic == null || !isActive()) { - Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); - result = 0; - } else { - result = ic.getCursorCapsMode(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode"); try { - callback.onResult(result); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final int result; + if (ic == null || !isActive()) { + Log.w(TAG, "getCursorCapsMode on inactive InputConnection"); + result = 0; + } else { + result = ic.getCursorCapsMode(msg.arg1); + } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetCursorCapsModeProto(msg.arg1, + result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getCursorCapsMode", getIMM(), icProto); + } + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to getCursorCapsMode()." + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_GET_EXTRACTED_TEXT: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText"); try { final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1; final IExtractedTextResultCallback callback = @@ -371,6 +440,12 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } else { result = ic.getExtractedText(request, msg.arg1); } + if (ImeTracing.getInstance().isEnabled()) { + icProto = InputConnectionHelper.buildGetExtractedTextProto(request, + msg.arg1, result); + ImeTracing.getInstance().triggerClientDump( + TAG + "#getExtractedText", getIMM(), icProto); + } try { callback.onResult(result); } catch (RemoteException e) { @@ -378,159 +453,237 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_COMMIT_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitText on inactive InputConnection"); + return; + } + ic.commitText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_SELECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setSelection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setSelection on inactive InputConnection"); + return; + } + ic.setSelection(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setSelection(msg.arg1, msg.arg2); return; } case DO_PERFORM_EDITOR_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performEditorAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performEditorAction on inactive InputConnection"); + return; + } + ic.performEditorAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performEditorAction(msg.arg1); return; } case DO_PERFORM_CONTEXT_MENU_ACTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "performContextMenuAction on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "performContextMenuAction on inactive InputConnection"); + return; + } + ic.performContextMenuAction(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.performContextMenuAction(msg.arg1); return; } case DO_COMMIT_COMPLETION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCompletion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCompletion on inactive InputConnection"); + return; + } + ic.commitCompletion((CompletionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCompletion((CompletionInfo)msg.obj); return; } case DO_COMMIT_CORRECTION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "commitCorrection on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "commitCorrection on inactive InputConnection"); + return; + } + ic.commitCorrection((CorrectionInfo) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.commitCorrection((CorrectionInfo)msg.obj); return; } case DO_SET_COMPOSING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingText on inactive InputConnection"); + return; + } + ic.setComposingText((CharSequence) msg.obj, msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingText((CharSequence)msg.obj, msg.arg1); return; } case DO_SET_COMPOSING_REGION: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "setComposingRegion on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "setComposingRegion on inactive InputConnection"); + return; + } + ic.setComposingRegion(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.setComposingRegion(msg.arg1, msg.arg2); return; } case DO_FINISH_COMPOSING_TEXT: { - if (isFinished()) { - // In this case, #finishComposingText() is guaranteed to be called already. - // There should be no negative impact if we ignore this call silently. - if (DEBUG) { - Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText"); + try { + if (isFinished()) { + // In this case, #finishComposingText() is guaranteed to be called already. + // There should be no negative impact if we ignore this call silently. + if (DEBUG) { + Log.w(TAG, "Bug 35301295: Redundant finishComposingText."); + } + return; } - return; - } - InputConnection ic = getInputConnection(); - // Note we do NOT check isActive() here, because this is safe - // for an IME to call at any time, and we need to allow it - // through to clean up our state after the IME has switched to - // another client. - if (ic == null) { - Log.w(TAG, "finishComposingText on inactive InputConnection"); - return; + InputConnection ic = getInputConnection(); + // Note we do NOT check isActive() here, because this is safe + // for an IME to call at any time, and we need to allow it + // through to clean up our state after the IME has switched to + // another client. + if (ic == null) { + Log.w(TAG, "finishComposingText on inactive InputConnection"); + return; + } + ic.finishComposingText(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.finishComposingText(); return; } case DO_SEND_KEY_EVENT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "sendKeyEvent on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "sendKeyEvent on inactive InputConnection"); + return; + } + ic.sendKeyEvent((KeyEvent) msg.obj); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.sendKeyEvent((KeyEvent)msg.obj); return; } case DO_CLEAR_META_KEY_STATES: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "clearMetaKeyStates on inactive InputConnection"); + return; + } + ic.clearMetaKeyStates(msg.arg1); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.clearMetaKeyStates(msg.arg1); return; } case DO_DELETE_SURROUNDING_TEXT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingText on inactive InputConnection"); + return; + } + ic.deleteSurroundingText(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingText(msg.arg1, msg.arg2); return; } case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, + "InputConnection#deleteSurroundingTextInCodePoints"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection"); + return; + } + ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2); return; } case DO_BEGIN_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "beginBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "beginBatchEdit on inactive InputConnection"); + return; + } + ic.beginBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.beginBatchEdit(); return; } case DO_END_BATCH_EDIT: { - InputConnection ic = getInputConnection(); - if (ic == null || !isActive()) { - Log.w(TAG, "endBatchEdit on inactive InputConnection"); - return; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit"); + try { + InputConnection ic = getInputConnection(); + if (ic == null || !isActive()) { + Log.w(TAG, "endBatchEdit on inactive InputConnection"); + return; + } + ic.endBatchEdit(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } - ic.endBatchEdit(); return; } case DO_PERFORM_PRIVATE_COMMAND: { final SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand"); try { final String action = (String) args.arg1; final Bundle data = (Bundle) args.arg2; @@ -541,25 +694,31 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } ic.performPrivateCommand(action, data); } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; } case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { - final IIntResultCallback callback = (IIntResultCallback) msg.obj; - final InputConnection ic = getInputConnection(); - final boolean result; - if (ic == null || !isActive()) { - Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); - result = false; - } else { - result = ic.requestCursorUpdates(msg.arg1); - } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates"); try { - callback.onResult(result ? 1 : 0); - } catch (RemoteException e) { - Log.w(TAG, "Failed to return the result to requestCursorUpdates()." - + " result=" + result, e); + final IIntResultCallback callback = (IIntResultCallback) msg.obj; + final InputConnection ic = getInputConnection(); + final boolean result; + if (ic == null || !isActive()) { + Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); + result = false; + } else { + result = ic.requestCursorUpdates(msg.arg1); + } + try { + callback.onResult(result ? 1 : 0); + } catch (RemoteException e) { + Log.w(TAG, "Failed to return the result to requestCursorUpdates()." + + " result=" + result, e); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } @@ -571,6 +730,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { if (isFinished()) { return; } + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection"); try { InputConnection ic = getInputConnection(); // Note we do NOT check isActive() here, because this is safe @@ -590,12 +750,14 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { mInputConnection = null; mFinished = true; } + Trace.traceEnd(Trace.TRACE_TAG_INPUT); } return; } case DO_COMMIT_CONTENT: { final int flags = msg.arg1; SomeArgs args = (SomeArgs) msg.obj; + Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent"); try { final IIntResultCallback callback = (IIntResultCallback) args.arg3; final InputConnection ic = getInputConnection(); @@ -620,6 +782,7 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { + " result=" + result, e); } } finally { + Trace.traceEnd(Trace.TRACE_TAG_INPUT); args.recycle(); } return; diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 33abbe82c109..e78ed4e211a7 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo; import com.android.internal.view.InputBindResult; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; +import com.android.internal.inputmethod.IInputBindResultResultCallback; /** * Public interface to the global input method manager, used by all client @@ -48,14 +49,15 @@ interface IInputMethodManager { // If windowToken is null, this just does startInput(). Otherwise this reports that a window // has gained focus, and if 'attribute' is non-null then also does startInput. // @NonNull - InputBindResult startInputOrWindowGainedFocus( + void startInputOrWindowGainedFocus( /* @StartInputReason */ int startInputReason, in IInputMethodClient client, in IBinder windowToken, /* @StartInputFlags */ int startInputFlags, /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode, int windowFlags, in EditorInfo attribute, IInputContext inputContext, /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags, - int unverifiedTargetSdkVersion); + int unverifiedTargetSdkVersion, + in IInputBindResultResultCallback inputBindResult); void showInputMethodPickerFromClient(in IInputMethodClient client, int auxiliarySubtypeMode); diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 8c763a6efe54..b70348a95384 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -24,6 +24,9 @@ import android.inputmethodservice.AbstractInputMethodService; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.util.imetracing.ImeTracing; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; @@ -87,8 +90,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextAfterCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextAfterCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextAfterCursor", + inputMethodService, icProto); + } + + return result; } /** @@ -107,8 +120,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getTextBeforeCursor()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetTextBeforeCursorProto(length, + flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getTextBeforeCursor", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -127,8 +150,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + CharSequence result = Completable.getResultOrNull( value, TAG, "getSelectedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSelectedTextProto(flags, + result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSelectedText", + inputMethodService, icProto); + } + + return result; } /** @@ -161,8 +194,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + SurroundingText result = Completable.getResultOrNull( value, TAG, "getSurroundingText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetSurroundingTextProto( + beforeLength, afterLength, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getSurroundingText", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -177,8 +220,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return 0; } - return Completable.getResultOrZero( + int result = Completable.getResultOrZero( value, TAG, "getCursorCapsMode()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetCursorCapsModeProto( + reqModes, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getCursorCapsMode", + inputMethodService, icProto); + } + + return result; } @AnyThread @@ -193,8 +246,18 @@ public class InputConnectionWrapper implements InputConnection { } catch (RemoteException e) { return null; } - return Completable.getResultOrNull( + ExtractedText result = Completable.getResultOrNull( value, TAG, "getExtractedText()", mCancellationGroup, MAX_WAIT_TIME_MILLIS); + + final AbstractInputMethodService inputMethodService = mInputMethodService.get(); + if (inputMethodService != null && ImeTracing.getInstance().isEnabled()) { + ProtoOutputStream icProto = InputConnectionHelper.buildGetExtractedTextProto( + request, flags, result); + ImeTracing.getInstance().triggerServiceDump(TAG + "#getExtractedText", + inputMethodService, icProto); + } + + return result; } @AnyThread diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index ff3543c837eb..6cdf0df17b65 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -16,20 +16,35 @@ package com.android.internal.widget; +import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE; +import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END; +import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START; + import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; import android.text.Editable; +import android.text.Selection; import android.text.method.KeyListener; import android.util.Log; +import android.util.imetracing.InputConnectionHelper; +import android.util.proto.ProtoOutputStream; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.widget.TextView; -public class EditableInputConnection extends BaseInputConnection { +/** + * Base class for an editable InputConnection instance. This is created by {@link TextView} or + * {@link EditText}. + */ +public class EditableInputConnection extends BaseInputConnection + implements DumpableInputConnection { private static final boolean DEBUG = false; private static final String TAG = "EditableInputConnection"; @@ -222,4 +237,28 @@ public class EditableInputConnection extends BaseInputConnection { } return true; } + + @Override + public void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + CharSequence editableText = mTextView.getText(); + CharSequence selectedText = getSelectedText(0 /* flags */); + if (InputConnectionHelper.DUMP_TEXT) { + if (editableText != null) { + proto.write(EDITABLE_TEXT, editableText.toString()); + } + if (selectedText != null) { + proto.write(SELECTED_TEXT, selectedText.toString()); + } + } + final Editable content = getEditable(); + if (content != null) { + int start = Selection.getSelectionStart(content); + int end = Selection.getSelectionEnd(content); + proto.write(SELECTED_TEXT_START, start); + proto.write(SELECTED_TEXT_END, end); + } + proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0)); + proto.end(token); + } } diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index b4e108faee2d..3f205c785258 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -16,69 +16,61 @@ package com.android.internal.widget; -import android.annotation.Nullable; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.ImageDecoder; +import android.graphics.drawable.AnimatedImageDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.util.Log; +import android.util.Size; import java.io.IOException; -import java.io.InputStream; -/** - * A class to extract Bitmaps from a MessagingStyle message. - */ +/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */ public class LocalImageResolver { private static final String TAG = LocalImageResolver.class.getSimpleName(); private static final int MAX_SAFE_ICON_SIZE_PX = 480; - @Nullable public static Drawable resolveImage(Uri uri, Context context) throws IOException { - BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context); - if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { - return null; - } - - int originalSize = - (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth) - ? onlyBoundsOptions.outHeight - : onlyBoundsOptions.outWidth; - - double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) - ? (originalSize / MAX_SAFE_ICON_SIZE_PX) - : 1.0; - - BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); - bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio); - InputStream input = context.getContentResolver().openInputStream(uri); - Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); - input.close(); - return new BitmapDrawable(context.getResources(), bitmap); + final ImageDecoder.Source source = + ImageDecoder.createSource(context.getContentResolver(), uri); + final Drawable drawable = + ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded); + return drawable; } - private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context) + public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight) throws IOException { - BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); - try (InputStream input = context.getContentResolver().openInputStream(uri)) { - if (input == null) { - throw new IllegalArgumentException(); + final ImageDecoder.Source source = + ImageDecoder.createSource(context.getContentResolver(), uri); + return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> { + final Size size = info.getSize(); + if (size.getWidth() > size.getHeight()) { + if (size.getWidth() > maxWidth) { + final int targetHeight = size.getHeight() * maxWidth / size.getWidth(); + decoder.setTargetSize(maxWidth, targetHeight); + } + } else { + if (size.getHeight() > maxHeight) { + final int targetWidth = size.getWidth() * maxHeight / size.getHeight(); + decoder.setTargetSize(targetWidth, maxHeight); + } } - onlyBoundsOptions.inJustDecodeBounds = true; - BitmapFactory.decodeStream(input, null, onlyBoundsOptions); - } catch (IllegalArgumentException iae) { - onlyBoundsOptions.outWidth = -1; - onlyBoundsOptions.outHeight = -1; - Log.e(TAG, "error loading image", iae); - } - return onlyBoundsOptions; + }); } private static int getPowerOfTwoForSampleRatio(double ratio) { - int k = Integer.highestOneBit((int) Math.floor(ratio)); + final int k = Integer.highestOneBit((int) Math.floor(ratio)); return Math.max(1, k); } + + private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, + ImageDecoder.Source source) { + final Size size = info.getSize(); + final int originalSize = Math.max(size.getHeight(), size.getWidth()); + final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) + ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX + : 1.0; + decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); + } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index b562ef838633..9712b4e794c5 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -311,6 +311,15 @@ public class LockPatternUtils { return getDevicePolicyManager().getPasswordMinimumMetrics(userId); } + /** + * Returns the effective complexity for the user. + * @param userId The user to return the complexity for. + * @return complexity level for the user. + */ + public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) { + return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId); + } + public int getRequestedPasswordQuality(int userId) { return getDevicePolicyManager().getPasswordQuality(null, userId); } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 4ddc782aacb4..bad21d28416b 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1090,6 +1090,20 @@ public class LockPatternView extends View { } } + /** + * Change theme colors + * @param regularColor The dot color + * @param successColor Color used when pattern is correct + * @param errorColor Color used when authentication fails + */ + public void setColors(int regularColor, int successColor, int errorColor) { + mRegularColor = regularColor; + mErrorColor = errorColor; + mSuccessColor = successColor; + mPathPaint.setColor(regularColor); + invalidate(); + } + private float getCenterXForColumn(int column) { return mPaddingLeft + column * mSquareWidth + mSquareWidth / 2f; } diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp index 5bd808b67c57..b70190fc5ec3 100644 --- a/core/jni/android_media_MicrophoneInfo.cpp +++ b/core/jni/android_media_MicrophoneInfo.cpp @@ -56,8 +56,8 @@ jint convertMicrophoneInfoFromNative(JNIEnv *env, jobject *jMicrophoneInfo, jobject jFrequencyResponses = NULL; jobject jChannelMappings = NULL; - jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string()); - jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string()); + jDeviceId = env->NewStringUTF(microphoneInfo->getDeviceId().c_str()); + jAddress = env->NewStringUTF(microphoneInfo->getAddress().c_str()); if (microphoneInfo->getGeometricLocation().size() != 3 || microphoneInfo->getOrientation().size() != 3) { jStatus = nativeToJavaStatus(BAD_VALUE); diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 5eeeb048e6d6..748b4b4f5743 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -26,17 +26,7 @@ hyunyoungs@google.com # Graphics stats jreck@google.com -# Temporary Block to assist in migration -# Bug: 143080132 -per-file *enums.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *media_output_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *networkcapabilities.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *data_stall_event.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *procstats_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *usb.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *network_stack.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *tethering.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *dns_resolver.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *device_policy.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *launcher.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com -per-file *mediametrics.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +# Accessibility +pweaver@google.com +hongmingjin@google.com +cbrower@google.com diff --git a/core/proto/android/inputmethodservice/inputmethodservice.proto b/core/proto/android/inputmethodservice/inputmethodservice.proto index e5d171361695..1f68fb4b513d 100644 --- a/core/proto/android/inputmethodservice/inputmethodservice.proto +++ b/core/proto/android/inputmethodservice/inputmethodservice.proto @@ -18,6 +18,7 @@ syntax = "proto2"; import "frameworks/base/core/proto/android/inputmethodservice/softinputwindow.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; package android.inputmethodservice; @@ -51,6 +52,7 @@ message InputMethodServiceProto { optional int32 status_icon = 25; optional InsetsProto last_computed_insets = 26; optional string settings_observer = 27; + optional .android.view.inputmethod.InputConnectionCallProto input_connection_call = 28; message InsetsProto { optional int32 content_top_insets = 1; diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto new file mode 100644 index 000000000000..d1f257ff2c5c --- /dev/null +++ b/core/proto/android/view/inputmethod/inputconnection.proto @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/privacy.proto"; + +package android.view.inputmethod; + +option java_multiple_files = true; + +/** + * Represents a {@link android.view.inputmethod.InputConnection} object. + */ +message InputConnectionProto { + optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selected_text_start = 3; + optional int32 selected_text_end = 4; + optional int32 cursor_caps_mode = 5; +} + +/** + * Shows information about parameters and result for method calls to + * {@link android.view.inputmethod.InputConnection}. + */ +message InputConnectionCallProto { + oneof method_call { + GetTextBeforeCursor get_text_before_cursor = 1; + GetTextAfterCursor get_text_after_cursor = 2; + GetSelectedText get_selected_text = 3; + GetSurroundingText get_surrounding_text = 4; + GetCursorCapsMode get_cursor_caps_mode = 5; + GetExtractedText get_extracted_text = 6; + } + + message GetTextBeforeCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetTextAfterCursor { + optional int32 length = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSelectedText { + optional int32 flags = 1; + optional string result = 2 [(.android.privacy).dest = DEST_LOCAL]; + } + + message GetSurroundingText { + optional int32 before_length = 1; + optional int32 after_length = 2; + optional int32 flags = 3; + optional SurroundingText result = 4; + + message SurroundingText { + optional string text = 1 [(.android.privacy).dest = DEST_LOCAL]; + optional int32 selection_start = 2; + optional int32 selection_end = 3; + optional int32 offset = 4; + } + } + + message GetCursorCapsMode { + optional int32 req_modes = 1; + optional int32 result = 2; + } + + message GetExtractedText { + optional ExtractedTextRequest request = 1; + optional int32 flags = 2; + optional string result = 3 [(.android.privacy).dest = DEST_LOCAL]; + + message ExtractedTextRequest { + optional int32 token = 1; + optional int32 flags = 2; + optional int32 hint_max_lines = 3; + optional int32 hint_max_chars = 4; + } + } +}
\ No newline at end of file diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto index 5c0f341cb9e4..8e4377ca124c 100644 --- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto +++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto @@ -24,6 +24,7 @@ import "frameworks/base/core/proto/android/view/viewrootimpl.proto"; import "frameworks/base/core/proto/android/view/insetscontroller.proto"; import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto"; import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto"; +import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto"; import "frameworks/base/core/proto/android/view/imefocuscontroller.proto"; import "frameworks/base/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto"; @@ -70,6 +71,8 @@ message InputMethodClientsTraceProto { optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5; optional EditorInfoProto editor_info = 6; optional ImeFocusControllerProto ime_focus_controller = 7; + optional InputConnectionProto input_connection = 8; + optional InputConnectionCallProto input_connection_call = 9; } } diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto index 4bb56f8acfa4..062485d304c3 100644 --- a/core/proto/android/view/windowlayoutparams.proto +++ b/core/proto/android/view/windowlayoutparams.proto @@ -35,7 +35,7 @@ message WindowLayoutParamsProto { optional int32 height = 5; optional float horizontal_margin = 6; optional float vertical_margin = 7; - optional int32 gravity = 8; + optional int32 gravity = 8 [(.android.typedef) = "android.view.Gravity.GravityFlags"]; optional int32 soft_input_mode = 9 [(.android.typedef) = "android.view.WindowManager.LayoutParams.SoftInputModeFlags"]; optional .android.graphics.PixelFormatProto.Format format = 10; optional int32 window_animations = 11; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 11a821468aa1..62d0a6b0a26f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -120,6 +120,12 @@ <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" /> + <protected-broadcast android:name="android.app.action.USER_ADDED" /> + <protected-broadcast android:name="android.app.action.USER_REMOVED" /> + <protected-broadcast android:name="android.app.action.USER_STARTED" /> + <protected-broadcast android:name="android.app.action.USER_STOPPED" /> + <protected-broadcast android:name="android.app.action.USER_SWITCHED" /> + <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" /> <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" /> @@ -1809,6 +1815,16 @@ <permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE" android:protectionLevel="signature|privileged" /> + <!-- @SystemApi @hide Allows system APK to update Wifi/Cellular coex channels to avoid. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" + android:protectionLevel="signature" /> + + <!-- @SystemApi @hide Allows applications to access Wifi/Cellular coex channels being avoided. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" + android:protectionLevel="signature|privileged" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> @@ -3104,6 +3120,12 @@ <permission android:name="android.permission.DUMP" android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to start tracing for InputMethod and WindowManager. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.CONTROL_UI_TRACING" + android:protectionLevel="signature|privileged|development" /> + <!-- Allows an application to read the low-level system log files. <p>Not for use by third-party applications, because Log entries can contain the user's private information. --> @@ -4190,6 +4212,14 @@ <permission android:name="android.permission.FACTORY_TEST" android:protectionLevel="signature" /> + <!-- @hide @TestApi Allows an application to broadcast the intent {@link + android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}. + <p>Not for use by third-party applications. + --> + <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" + android:protectionLevel="signature|recents" /> + <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" /> + <!-- Allows an application to broadcast a notification that an application package has been removed. <p>Not for use by third-party applications. @@ -5225,7 +5255,8 @@ <attribution android:tag="TwilightService" android:label="@string/twilight_service"/> <!-- Attribution for the Offline LocationTimeZoneProvider, used to detect time zone using on-device data --> - <attribution android:tag="OfflineLocationTimeZoneProvider" android:label="@string/offline_location_time_zone_detection_service"/> + <attribution android:tag="OfflineLocationTimeZoneProvider" + android:label="@string/offline_location_time_zone_detection_service_attribution"/> <application android:process="system" android:persistent="true" diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index b0ee12a520d9..88998f2167a8 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -26,6 +26,18 @@ android:theme="@style/Theme.DeviceDefault.Notification" > + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_left_icon_size" + android:layout_height="@dimen/notification_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:layout_width="@dimen/notification_icon_circle_size" diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 69d4a12f4d69..000638475a10 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -23,6 +23,18 @@ android:tag="base" > + <ImageView + android:id="@+id/left_icon" + android:layout_width="@dimen/notification_left_icon_size" + android:layout_height="@dimen/notification_left_icon_size" + android:layout_gravity="center_vertical|start" + android:layout_marginStart="@dimen/notification_left_icon_start" + android:background="@drawable/notification_large_icon_outline" + android:importantForAccessibility="no" + android:scaleType="centerCrop" + android:visibility="gone" + /> + <com.android.internal.widget.CachingIconView android:id="@+id/icon" android:layout_width="@dimen/notification_icon_circle_size" @@ -34,7 +46,7 @@ /> <LinearLayout - android:id="@+id/notification_standard_view_column" + android:id="@+id/notification_headerless_view_column" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml index 39cdce9cc46b..29f2b6f14b57 100644 --- a/core/res/res/values-night/colors.xml +++ b/core/res/res/values-night/colors.xml @@ -26,9 +26,6 @@ <color name="notification_default_color_dark">#ddffffff</color> - <!-- The background color of a notification card. --> - <color name="notification_material_background_color">@color/black</color> - <color name="chooser_row_divider">@color/list_divider_color_dark</color> <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color> <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color> diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml index 4e6b712f1f5d..952cdd08451c 100644 --- a/core/res/res/values-night/values.xml +++ b/core/res/res/values-night/values.xml @@ -25,18 +25,12 @@ <item name="colorControlNormal">?attr/textColorPrimary</item> <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item> <item name="forceDarkAllowed">false</item> - - <!-- QS panel background --> - <item name="colorBackgroundFloating">@color/black</item> - - <!-- volume background --> - <item name="panelColorBackground">@color/material_grey_800</item> </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" /> <style name="TextAppearance.Material.Notification"> - <item name="textColor">@color/notification_secondary_text_color_dark</item> + <item name="textColor">?attr/textColorPrimary</item> <item name="textSize">@dimen/notification_text_size</item> </style> </resources>
\ No newline at end of file diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 1242c6dc8217..0079d8cd0276 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -143,8 +143,6 @@ <color name="notification_default_color_dark">@color/primary_text_default_material_light</color> <color name="notification_default_color_light">#a3202124</color> - <color name="notification_material_background_color">#ffffffff</color> - <color name="notification_default_color">#757575</color> <!-- Gray 600 --> <color name="notification_action_button_text_color">@color/notification_default_color</color> diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 7a8f411992ce..310ca893ac39 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -37,10 +37,10 @@ <color name="accent_device_default_dark">@color/accent_material_dark</color> <color name="accent_device_default">@color/accent_device_default_light</color> - <color name="background_device_default_dark">@color/background_material_dark</color> - <color name="background_device_default_light">@color/background_material_light</color> - <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color> - <color name="background_floating_device_default_light">@color/background_floating_material_light</color> + <color name="background_device_default_dark">#1A1A1A</color> + <color name="background_device_default_light">#F2F2F2</color> + <color name="background_floating_device_default_dark">#0D0D0D</color> + <color name="background_floating_device_default_light">#CCCCCC</color> <!-- Error color --> <color name="error_color_device_default_dark">@color/error_color_material_dark</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c9f140df68cb..37c3adbe5fcc 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1559,6 +1559,14 @@ <!-- Class name of WallpaperManagerService. --> <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string> + <!-- Specifies priority of automatic time sources. Suggestions from higher entries in the list + take precedence over lower ones. + See com.android.server.timedetector.TimeDetectorStrategy for available sources. --> + <string-array name="config_autoTimeSourcesPriority"> + <item>telephony</item> + <item>network</item> + </string-array> + <!-- Enables the TimeZoneRuleManager service. This is the global switch for the updateable time zone update mechanism. --> <bool name="config_enableUpdateableTimeZoneRules">false</bool> @@ -4239,6 +4247,35 @@ If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> <integer name="config_defaultRefreshRateInZone">0</integer> + <!-- The display uses different gamma curves for different refresh rates. It's hard for panel + vendor to tune the curves to have exact same brightness for different refresh rate. So + flicker could be observed at switch time. The issue can be observed on the screen with + even full white content at the high brightness. To prevent flickering, we support fixed + refresh rates if the display and ambient brightness are equal to or above the provided + thresholds. You can define multiple threshold levels as higher brightness environments + may have lower display brightness requirements for the flickering is visible. And the + high brightness environment could have higher threshold. + For example, fixed refresh rate if + display brightness >= disp0 && ambient brightness >= amb0 + || display brightness >= disp1 && ambient brightness >= amb1 --> + <integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate"> + <!-- + <item>disp0</item> + <item>disp1</item> + --> + </integer-array> + + <integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate"> + <!-- + <item>amb0</item> + <item>amb1</item> + --> + </integer-array> + + <!-- Default refresh rate in the high zone defined by brightness and ambient thresholds. + If non-positive, then the refresh rate is unchanged even if thresholds are configured. --> + <integer name="config_fixedRefreshRateInHighZone">0</integer> + <!-- The type of the light sensor to be used by the display framework for things like auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. --> <string name="config_displayLightSensorType" translatable="false" /> @@ -4522,4 +4559,7 @@ <!-- If true, hide the display cutout with display area --> <bool name="config_hideDisplayCutoutWithDisplayArea">false</bool> + + <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. --> + <bool name="config_trackerAppNeedsPermissions">false</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 19591f6a666f..4bcabff109ea 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -714,6 +714,10 @@ <dimen name="notification_right_icon_headerless_margin">12dp</dimen> <!-- The top margin of the right icon in the "big" notification states --> <dimen name="notification_right_icon_big_margin_top">16dp</dimen> + <!-- The size of the left icon --> + <dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen> + <!-- The left padding of the left icon --> + <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen> <!-- The alpha of a disabled notification button --> <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item> diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml index f77c6f99c063..a12d2a951460 100644 --- a/core/res/res/values/ids.xml +++ b/core/res/res/values/ids.xml @@ -194,6 +194,12 @@ <!-- A tag used to save the index where the custom view is stored --> <item type="id" name="notification_custom_view_index_tag" /> + <!-- A tag used to store the margin end for this view when the right icon is visible --> + <item type="id" name="tag_margin_end_when_icon_gone" /> + + <!-- A tag used to store the margin end for this view when the right icon is gone --> + <item type="id" name="tag_margin_end_when_icon_visible" /> + <!-- Marks the "copy to clipboard" button in the ChooserActivity --> <item type="id" name="chooser_copy_button" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8e3a0cbdd10c..89b986b8fcb8 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -433,9 +433,12 @@ <string name="sensor_notification_service">Sensor Notification Service</string> <!-- Attribution for Twilight service. [CHAR LIMIT=NONE]--> <string name="twilight_service">Twilight Service</string> - <!-- Attribution for Offline LocationTimeZoneDetector service, i.e. one capable of performing - time zone lookup using geo-spacial information held on the device. [CHAR LIMIT=NONE]--> - <string name="offline_location_time_zone_detection_service">Offline Time Zone Detection Service</string> + <!-- Attribution for the Offline LocationTimeZoneProvider service, i.e. the service capable of + performing time zone detection using time zone geospatial information held on the device. + This text is shown in UIs related to an application name to help users and developers to + understand which sub-unit of an application is requesting permissions and using power. + [CHAR LIMIT=NONE]--> + <string name="offline_location_time_zone_detection_service_attribution">Time Zone Detector (No connectivity)</string> <!-- Factory reset warning dialog strings--> <skip /> <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 40aae9e4e085..e96439250696 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -218,7 +218,7 @@ <java-symbol type="id" name="inbox_text6" /> <java-symbol type="id" name="status_bar_latest_event_content" /> <java-symbol type="id" name="notification_main_column" /> - <java-symbol type="id" name="notification_standard_view_column" /> + <java-symbol type="id" name="notification_headerless_view_column" /> <java-symbol type="id" name="sms_short_code_confirm_message" /> <java-symbol type="id" name="sms_short_code_detail_layout" /> <java-symbol type="id" name="sms_short_code_detail_message" /> @@ -2166,6 +2166,7 @@ <java-symbol type="string" name="config_defaultNetworkScorerPackageName" /> <java-symbol type="string" name="config_persistentDataPackageName" /> <java-symbol type="string" name="config_deviceConfiguratorPackageName" /> + <java-symbol type="array" name="config_autoTimeSourcesPriority" /> <java-symbol type="bool" name="config_enableGeolocationTimeZoneDetection" /> <java-symbol type="bool" name="config_enablePrimaryLocationTimeZoneOverlay" /> <java-symbol type="string" name="config_primaryLocationTimeZoneProviderPackageName" /> @@ -3002,7 +3003,6 @@ <java-symbol type="string" name="usb_mtp_launch_notification_description" /> <java-symbol type="color" name="notification_action_list" /> - <java-symbol type="color" name="notification_material_background_color" /> <!-- Resolver target actions --> <java-symbol type="array" name="resolver_target_actions_pin" /> @@ -3070,6 +3070,8 @@ <java-symbol type="dimen" name="notification_media_image_margin_end" /> <java-symbol type="id" name="notification_action_list_margin_target" /> <java-symbol type="dimen" name="notification_action_disabled_alpha" /> + <java-symbol type="id" name="tag_margin_end_when_icon_visible" /> + <java-symbol type="id" name="tag_margin_end_when_icon_gone" /> <!-- Override Wake Key Behavior When Screen is Off --> <java-symbol type="bool" name="config_wakeOnDpadKeyPress" /> @@ -3811,6 +3813,11 @@ <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> + <!-- For fixed refresh rate displays in high brightness--> + <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" /> + <java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" /> + <java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" /> + <!-- For Auto-Brightness --> <java-symbol type="string" name="config_displayLightSensorType" /> @@ -4103,4 +4110,7 @@ <java-symbol type="string" name="window_magnification_prompt_content" /> <java-symbol type="string" name="turn_on_magnification_settings_action" /> <java-symbol type="string" name="dismiss_action" /> + + <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/> + </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 1afaf4f7f184..c0731c82bff3 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -215,8 +215,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item> <item name="colorAccent">@color/accent_device_default_dark</item> <item name="colorError">@color/error_color_device_default_dark</item> + <item name="colorBackground">@color/background_device_default_dark</item> <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item> <item name="colorPopupBackground">?attr/colorBackgroundFloating</item> + <item name="panelColorBackground">?attr/colorBackgroundFloating</item> </style> <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" /> @@ -943,8 +945,10 @@ easier. <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item> <item name="colorAccent">@color/accent_device_default_light</item> <item name="colorError">@color/error_color_device_default_light</item> + <item name="colorBackground">@color/background_device_default_light</item> <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item> <item name="colorPopupBackground">?attr/colorBackgroundFloating</item> + <item name="panelColorBackground">?attr/colorBackgroundFloating</item> </style> <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an @@ -1517,9 +1521,6 @@ easier. <!-- Toolbar attributes --> <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item> - - <!-- volume background --> - <item name="panelColorBackground">@color/primary_material_light</item> </style> <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Light.Dialog"> diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml index 5fd8a95af64d..71dfc551ccc1 100644 --- a/core/res/res/xml/config_user_types.xml +++ b/core/res/res/xml/config_user_types.xml @@ -40,7 +40,7 @@ The following example modifies two AOSP user types (the FULL user android.os.use and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type (com.example.profilename): -<user-types> +<user-types version="0"> <full-type name="android.os.usertype.full.SECONDARY" > <default-restrictions no_sms="true" /> </full-type> @@ -65,6 +65,11 @@ and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROF <profile-type name="com.example.profilename" max-allowed-per-parent="2" /> + + <change-user-type + from="android.os.usertype.profile.MANAGED" + to="com.example.profilename" + whenVersionLeq="1" /> </user-types> Mandatory attributes: @@ -93,6 +98,10 @@ If this file is updated, the properties of any pre-existing user types will be u Note, however, that default-restrictions refers to the restrictions applied at the time of user creation; therefore, the active restrictions of any pre-existing users will not be updated. +The 'change-user-type' tag should be used in conjunction with the 'version' property of +'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to' +type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'. + --> <user-types> </user-types> diff --git a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java index 27584a597cc8..7552ec404c13 100644 --- a/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java +++ b/core/tests/coretests/src/android/app/people/PeopleSpaceTileTest.java @@ -21,11 +21,18 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; import android.app.Notification; import android.content.Context; import android.content.Intent; +import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.UserHandle; @@ -38,26 +45,37 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest public class PeopleSpaceTileTest { private Context mContext; + private final Drawable mDrawable = new ColorDrawable(Color.BLUE); + private final Icon mIcon = PeopleSpaceTile.convertDrawableToIcon(mDrawable); + + @Mock + private LauncherApps mLauncherApps; @Before public void setUp() { mContext = InstrumentationRegistry.getContext(); + MockitoAnnotations.initMocks(this); + when(mLauncherApps.getShortcutIconDrawable(any(), eq(0))).thenReturn(mDrawable); } @Test public void testId() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertThat(tile.getId()).isEqualTo("123"); - tile = new PeopleSpaceTile.Builder(new ShortcutInfo.Builder(mContext, "123").build()).setId( - "5").build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setId("5") + .build(); assertThat(tile.getId()).isEqualTo("5"); tile = new PeopleSpaceTile.Builder("12", null, null, null).build(); @@ -67,11 +85,13 @@ public class PeopleSpaceTileTest { @Test public void testUserName() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertThat(tile.getUserName()).isNull(); - tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setUserName("Name 1").build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setUserName("Name 1") + .build(); assertThat(tile.getUserName()).isEqualTo("Name 1"); tile = new PeopleSpaceTile.Builder(null, "Name 2", null, null).build(); @@ -81,21 +101,19 @@ public class PeopleSpaceTileTest { @Test public void testUserIcon() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setUserIcon( - Icon.createWithResource(mContext, 1)).build(); - assertThat(tile.getUserIcon().toString()).isEqualTo( - Icon.createWithResource(mContext, 1).toString()); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setUserIcon( + mIcon).build(); + assertThat(tile.getUserIcon().toString()).isEqualTo(mIcon.toString()); - tile = new PeopleSpaceTile.Builder("12", null, Icon.createWithResource(mContext, 2), + tile = new PeopleSpaceTile.Builder("12", null, mIcon, null).build(); - assertThat(tile.getUserIcon().toString()).isEqualTo( - Icon.createWithResource(mContext, 2).toString()); + assertThat(tile.getUserIcon().toString()).isEqualTo(mIcon.toString()); } @Test public void testContactUri() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setContactUri( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setContactUri( Uri.parse("test")).build(); assertThat(tile.getContactUri()).isEqualTo(Uri.parse("test")); @@ -103,8 +121,10 @@ public class PeopleSpaceTileTest { @Test public void testUid() { - PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setUid(42).build(); + PeopleSpaceTile tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setUid(42) + .build(); assertThat(tile.getUid()).isEqualTo(42); } @@ -112,12 +132,12 @@ public class PeopleSpaceTileTest { @Test public void testPackageName() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); // Automatically added by creating a ShortcutInfo. assertThat(tile.getPackageName()).isEqualTo("com.android.frameworks.coretests"); tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setPackageName( + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).setPackageName( "package.name").build(); assertThat(tile.getPackageName()).isEqualTo("package.name"); @@ -129,36 +149,39 @@ public class PeopleSpaceTileTest { @Test public void testLastInteractionTimestamp() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertThat(tile.getLastInteractionTimestamp()).isEqualTo(0L); - tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setLastInteractionTimestamp( - 7L).build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setLastInteractionTimestamp(7L) + .build(); assertThat(tile.getLastInteractionTimestamp()).isEqualTo(7L); } @Test public void testImportantConversation() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertFalse(tile.isImportantConversation()); - tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setIsImportantConversation( - true).build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setIsImportantConversation(true) + .build(); assertTrue(tile.isImportantConversation()); } @Test public void testHiddenConversation() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertFalse(tile.isHiddenConversation()); - tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setIsHiddenConversation( - true).build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setIsHiddenConversation(true) + .build(); assertTrue(tile.isHiddenConversation()); } @@ -168,8 +191,10 @@ public class PeopleSpaceTileTest { StatusBarNotification sbn = new StatusBarNotification("pkg" /* pkg */, "pkg" /* opPkg */, 1 /* id */, "" /* tag */, 0 /* uid */, 0 /* initialPid */, 0 /* score */, notification, UserHandle.CURRENT, 0 /* postTime */); - PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setNotification(sbn).build(); + PeopleSpaceTile tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setNotification(sbn) + .build(); assertThat(tile.getNotification()).isEqualTo(sbn); } @@ -177,11 +202,13 @@ public class PeopleSpaceTileTest { @Test public void testIntent() { PeopleSpaceTile tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).build(); + new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps).build(); assertThat(tile.getIntent()).isNull(); - tile = new PeopleSpaceTile.Builder( - new ShortcutInfo.Builder(mContext, "123").build()).setIntent(new Intent()).build(); + tile = new PeopleSpaceTile + .Builder(new ShortcutInfo.Builder(mContext, "123").build(), mLauncherApps) + .setIntent(new Intent()) + .build(); assertThat(tile.getIntent().toString()).isEqualTo(new Intent().toString()); tile = new PeopleSpaceTile.Builder("12", null, null, new Intent()).build(); diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 5871e2e04687..7e992989426d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -52,6 +52,7 @@ import android.os.Parcelable; import android.os.PersistableBundle; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.SharedMemory; import android.platform.test.annotations.Presubmit; import android.view.DisplayAdjustments.FixedRotationAdjustments; import android.view.DisplayCutout; @@ -440,7 +441,8 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges) + AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges, + SharedMemory serializedSystemFontMap) throws RemoteException { } diff --git a/core/tests/coretests/src/android/widget/NumberPickerTest.java b/core/tests/coretests/src/android/widget/NumberPickerTest.java new file mode 100644 index 000000000000..cab7c89f4ca1 --- /dev/null +++ b/core/tests/coretests/src/android/widget/NumberPickerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.platform.test.annotations.Presubmit; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeProvider; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class NumberPickerTest { + @Test + public void testAccessibilityFocusedProperty() { + final int virtualViewIdIncrement = 1; + final int VirtualViewIdInput = 2; + final int VirtualViewIdDecrement = 3; + final NumberPicker np = + new NumberPicker(InstrumentationRegistry.getInstrumentation().getContext()); + final AccessibilityNodeProvider provider = np.getAccessibilityNodeProvider(); + + AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(View.NO_ID); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + info = provider.createAccessibilityNodeInfo(View.NO_ID); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + virtualViewIdIncrement, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(VirtualViewIdInput); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + VirtualViewIdInput, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(VirtualViewIdInput); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + + info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement); + assertFalse(info.isAccessibilityFocused()); + info.recycle(); + provider.performAction( + VirtualViewIdDecrement, + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, + null + ); + info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement); + assertTrue(info.isAccessibilityFocused()); + info.recycle(); + } +} diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java index 7b9283b41ff0..9978648ee32e 100644 --- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java +++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java @@ -16,11 +16,11 @@ package android.widget; -import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL; -import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP; -import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD; -import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT; +import static android.view.ContentInfo.SOURCE_AUTOFILL; +import static android.view.ContentInfo.SOURCE_CLIPBOARD; +import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP; +import static android.view.ContentInfo.SOURCE_INPUT_METHOD; +import static android.view.ContentInfo.SOURCE_PROCESS_TEXT; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static androidx.test.espresso.Espresso.onView; @@ -41,7 +41,7 @@ import android.content.ClipData; import android.content.ClipDescription; import android.net.Uri; import android.os.Bundle; -import android.view.OnReceiveContentListener; +import android.view.ContentInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnectionWrapper; import android.view.inputmethod.InputContentInfo; @@ -133,8 +133,8 @@ public class TextViewOnReceiveContentTest { // InputConnection.commitContent. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verify(ic.mMock, times(1)) .commitContent(any(InputContentInfo.class), eq(0), eq(null)); @@ -155,8 +155,8 @@ public class TextViewOnReceiveContentTest { // Invoke the listener and assert that the InputConnection is not invoked. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); } @@ -176,20 +176,20 @@ public class TextViewOnReceiveContentTest { // trigger calls to InputConnection.commitContent. ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); - OnReceiveContentListener.Payload payload = - new OnReceiveContentListener.Payload.Builder(clip, SOURCE_CLIPBOARD).build(); + ContentInfo payload = + new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD).build(); + payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build(); + payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); - payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); + payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build(); mDefaultReceiver.onReceiveContent(mEditText, payload); verifyZeroInteractions(ic.mMock); } diff --git a/core/tests/hdmitests/Android.bp b/core/tests/hdmitests/Android.bp index 2194d4b030ce..4755e0ea5259 100644 --- a/core/tests/hdmitests/Android.bp +++ b/core/tests/hdmitests/Android.bp @@ -19,6 +19,7 @@ android_test { static_libs: [ "androidx.test.rules", "frameworks-base-testutils", + "guava-android-testlib", "truth-prebuilt", ], libs: ["android.test.runner"], diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java new file mode 100755 index 000000000000..4c0de629c464 --- /dev/null +++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiDeviceInfoTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.hdmi; + +import androidx.test.filters.SmallTest; + +import com.google.common.testing.EqualsTester; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link HdmiDeviceInfo} */ +@RunWith(JUnit4.class) +@SmallTest +public class HdmiDeviceInfoTest { + + @Test + public void testEquals() { + int logicalAddr = 0x00; + int phyAddr = 0x1000; + int portId = 1; + int deviceType = 0; + int vendorId = 0x123456; + String displayName = "test device"; + int powerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY; + int deviceId = 3; + int adopterId = 2; + + new EqualsTester() + .addEqualityGroup(new HdmiDeviceInfo()) + .addEqualityGroup( + new HdmiDeviceInfo(phyAddr, portId), new HdmiDeviceInfo(phyAddr, portId)) + .addEqualityGroup( + new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId), + new HdmiDeviceInfo(phyAddr, portId, adopterId, deviceId)) + .addEqualityGroup( + new HdmiDeviceInfo( + logicalAddr, phyAddr, portId, deviceType, vendorId, displayName), + new HdmiDeviceInfo( + logicalAddr, phyAddr, portId, deviceType, vendorId, displayName)) + .addEqualityGroup( + new HdmiDeviceInfo( + logicalAddr, + phyAddr, + portId, + deviceType, + vendorId, + displayName, + powerStatus), + new HdmiDeviceInfo( + logicalAddr, + phyAddr, + portId, + deviceType, + vendorId, + displayName, + powerStatus)) + .testEquals(); + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 8406fdf99360..1fb63f04ccf5 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -322,6 +322,7 @@ applications that come with the platform <permission name="android.permission.DELETE_CACHE_FILES"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.DUMP"/> + <permission name="android.permission.CONTROL_UI_TRACING"/> <permission name="android.permission.ACTIVITY_EMBEDDING"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> <permission name="android.permission.GET_APP_OPS_STATS"/> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index a52eca7e0d73..6bcab8a34e2c 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2749,6 +2749,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1105210816": { + "message": "Skipping config check in destroyed state %s", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "1112047265": { "message": "finishDrawingWindow: %s mDrawState=%s", "level": "DEBUG", diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 36ef0a48fa20..24987daf87b5 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -81,6 +81,9 @@ public class Typeface { private static String TAG = "Typeface"; + /** @hide */ + public static final boolean ENABLE_LAZY_TYPEFACE_INITIALIZATION = false; + private static final NativeAllocationRegistry sRegistry = NativeAllocationRegistry.createMalloced( Typeface.class.getClassLoader(), nativeGetReleaseFunc()); @@ -1329,7 +1332,9 @@ public class Typeface { } static { - loadPreinstalledSystemFontMap(); + if (!ENABLE_LAZY_TYPEFACE_INITIALIZATION) { + loadPreinstalledSystemFontMap(); + } } @Override diff --git a/keystore/java/android/security/AppUriAuthenticationPolicy.java b/keystore/java/android/security/AppUriAuthenticationPolicy.java index 30f5a94ca0c8..0244ce97c0d4 100644 --- a/keystore/java/android/security/AppUriAuthenticationPolicy.java +++ b/keystore/java/android/security/AppUriAuthenticationPolicy.java @@ -28,8 +28,10 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.Set; /** * The app-URI authentication policy is set by the credential management app. This policy determines @@ -223,4 +225,17 @@ public final class AppUriAuthenticationPolicy implements Parcelable { } } + /** + * Get the set of aliases found in the policy. + * + * @hide + */ + public Set<String> getAliases() { + Set<String> aliases = new HashSet<>(); + for (UrisToAliases appsToUris : mAppToUris.values()) { + aliases.addAll(appsToUris.getUrisToAliases().values()); + } + return aliases; + } + } diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 97da3cc6f80f..add52fa5b436 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -18,6 +18,8 @@ package android.security; import android.content.pm.StringParceledListSlice; import android.security.keymaster.KeymasterCertificateChain; import android.security.keystore.ParcelableKeyGenParameterSpec; +import android.security.AppUriAuthenticationPolicy; +import android.net.Uri; /** * Caller is required to ensure that {@link KeyStore#unlock @@ -46,6 +48,7 @@ interface IKeyChainService { boolean installKeyPair( in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid); boolean removeKeyPair(String alias); + boolean containsKeyPair(String alias); // APIs used by Settings boolean deleteCaCertificate(String alias); @@ -55,6 +58,13 @@ interface IKeyChainService { boolean containsCaAlias(String alias); byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem); List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem); + void setCredentialManagementApp(String packageName, in AppUriAuthenticationPolicy policy); + void updateCredentialManagementAppPolicy(in AppUriAuthenticationPolicy policy); + boolean hasCredentialManagementApp(); + String getCredentialManagementAppPackageName(); + AppUriAuthenticationPolicy getCredentialManagementAppPolicy(); + String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri); + void removeCredentialManagementApp(); // APIs used by KeyChainActivity void setGrant(int uid, String alias, boolean value); diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 39e32c694d2e..856c9c2484f3 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -125,9 +125,10 @@ android_library { "protolog-lib", "SettingsLib", "WindowManager-Shell-proto", + "jsr330" ], kotlincflags: ["-Xjvm-default=enable"], manifest: "AndroidManifest.xml", min_sdk_version: "26", -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java index 357f777e1270..176c620fa119 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/FlingAnimationUtils.java @@ -23,6 +23,8 @@ import android.view.ViewPropertyAnimator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import javax.inject.Inject; + /** * Utility class to calculate general fling animation when the finger is released. */ @@ -368,6 +370,7 @@ public class FlingAnimationUtils { float mX2; float mY2; + @Inject public Builder(DisplayMetrics displayMetrics) { mDisplayMetrics = displayMetrics; reset(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index f199072f7bca..d3032f83fc1c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -136,13 +136,6 @@ class AppPair implements ShellTaskOrganizer.TaskListener { mAppPairLayout = null; } - void setVisible(boolean visible) { - if (mAppPairLayout == null) { - return; - } - mAppPairLayout.setDividerVisibility(visible); - } - @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) { @@ -160,32 +153,41 @@ class AppPair implements ShellTaskOrganizer.TaskListener { if (mTaskLeash1 == null || mTaskLeash2 == null) return; - setVisible(true); + mAppPairLayout.init(); final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); final Rect dividerBounds = mAppPairLayout.getDividerBounds(); // TODO: Is there more we need to do here? - mSyncQueue.runInSync(t -> t - .setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, - mTaskInfo1.positionInParent.y) - .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, - mTaskInfo2.positionInParent.y) - .setLayer(dividerLeash, Integer.MAX_VALUE) - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) - .show(mRootTaskLeash) - .show(dividerLeash) - .show(mTaskLeash1) - .show(mTaskLeash2)); + mSyncQueue.runInSync(t -> { + t.setPosition(mTaskLeash1, mTaskInfo1.positionInParent.x, + mTaskInfo1.positionInParent.y) + .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, + mTaskInfo2.positionInParent.y) + .setLayer(dividerLeash, Integer.MAX_VALUE) + .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .show(mRootTaskLeash) + .show(mTaskLeash1) + .show(mTaskLeash2); + }); } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { if (taskInfo.taskId == getRootTaskId()) { + if (mRootTaskInfo.isVisible != taskInfo.isVisible) { + mSyncQueue.runInSync(t -> { + if (taskInfo.isVisible) { + t.show(mRootTaskLeash); + } else { + t.hide(mRootTaskLeash); + } + }); + } mRootTaskInfo = taskInfo; if (mAppPairLayout != null && mAppPairLayout.updateConfiguration(mRootTaskInfo.configuration)) { - // Update bounds when there is root bounds or orientation changed. + // Update bounds when root bounds or its orientation changed. final WindowContainerTransaction wct = new WindowContainerTransaction(); final SurfaceControl dividerLeash = mAppPairLayout.getDividerLeash(); final Rect dividerBounds = mAppPairLayout.getDividerBounds(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java index f8703f7ec0bc..8c8655e1ff1f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairLayout.java @@ -16,7 +16,6 @@ package com.android.wm.shell.apppairs; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; @@ -45,21 +44,18 @@ import com.android.wm.shell.R; /** * Records and handles layout of a pair of apps. */ -// TODO(172704238): add tests final class AppPairLayout { private static final String DIVIDER_WINDOW_TITLE = "AppPairDivider"; - private final Context mContext; - private final AppPairWindowManager mAppPairWindowManager; - private final SurfaceControlViewHost mViewHost; - + private final Display mDisplay; private final int mDividerWindowWidth; private final int mDividerWindowInsets; + private final AppPairWindowManager mAppPairWindowManager; - private boolean mIsLandscape; + private Context mContext; private Rect mRootBounds; private DIVIDE_POLICY mDividePolicy; - private DividerView mDividerView; + private SurfaceControlViewHost mViewHost; private SurfaceControl mDividerLeash; AppPairLayout( @@ -68,7 +64,7 @@ final class AppPairLayout { Configuration configuration, SurfaceControl rootLeash) { mContext = context.createConfigurationContext(configuration); - mIsLandscape = isLandscape(configuration); + mDisplay = display; mRootBounds = configuration.windowConfiguration.getBounds(); mDividerWindowWidth = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); @@ -76,26 +72,22 @@ final class AppPairLayout { com.android.internal.R.dimen.docked_stack_divider_insets); mAppPairWindowManager = new AppPairWindowManager(configuration, rootLeash); - mViewHost = new SurfaceControlViewHost(mContext, display, mAppPairWindowManager); mDividePolicy = DIVIDE_POLICY.MIDDLE; - mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets); + mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); } boolean updateConfiguration(Configuration configuration) { mAppPairWindowManager.setConfiguration(configuration); final Rect rootBounds = configuration.windowConfiguration.getBounds(); - final boolean isLandscape = isLandscape(configuration); - if (mIsLandscape == isLandscape && isIdenticalBounds(mRootBounds, rootBounds)) { + if (isIdenticalBounds(mRootBounds, rootBounds)) { return false; } - mIsLandscape = isLandscape; + mContext = mContext.createConfigurationContext(configuration); mRootBounds = rootBounds; - mDividePolicy.update(mIsLandscape, mRootBounds, mDividerWindowWidth, mDividerWindowInsets); - mViewHost.relayout( - mDividePolicy.mDividerBounds.width(), - mDividePolicy.mDividerBounds.height()); - // TODO(172704238): handle divider bar rotation. + mDividePolicy.update(mRootBounds, mDividerWindowWidth, mDividerWindowInsets); + release(); + init(); return true; } @@ -116,22 +108,19 @@ final class AppPairLayout { } void release() { - if (mViewHost == null) return; + if (mViewHost == null) { + return; + } mViewHost.release(); + mDividerLeash = null; + mViewHost = null; } - void setDividerVisibility(boolean visible) { - if (mDividerView == null) { - initDivider(); - } - if (visible) { - mDividerView.show(); - } else { - mDividerView.hide(); + void init() { + if (mViewHost == null) { + mViewHost = new SurfaceControlViewHost(mContext, mDisplay, mAppPairWindowManager); } - } - private void initDivider() { final DividerView dividerView = (DividerView) LayoutInflater.from(mContext) .inflate(R.layout.split_divider, null); @@ -147,14 +136,9 @@ final class AppPairLayout { lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; mViewHost.setView(dividerView, lp); - mDividerView = dividerView; mDividerLeash = mAppPairWindowManager.getSurfaceControl(mViewHost.getWindowToken()); } - private static boolean isLandscape(Configuration configuration) { - return configuration.orientation == ORIENTATION_LANDSCAPE; - } - private static boolean isIdenticalBounds(Rect bounds1, Rect bounds2) { return bounds1.left == bounds2.left && bounds1.top == bounds2.top && bounds1.right == bounds2.right && bounds1.bottom == bounds2.bottom; @@ -167,8 +151,7 @@ final class AppPairLayout { enum DIVIDE_POLICY { MIDDLE; - void update(boolean isLandscape, Rect rootBounds, int dividerWindowWidth, - int dividerWindowInsets) { + void update(Rect rootBounds, int dividerWindowWidth, int dividerWindowInsets) { final int dividerOffset = dividerWindowWidth / 2; final int boundsOffset = dividerOffset - dividerWindowInsets; @@ -179,7 +162,7 @@ final class AppPairLayout { switch (this) { case MIDDLE: default: - if (isLandscape) { + if (isLandscape(rootBounds)) { mDividerBounds.left = rootBounds.width() / 2 - dividerOffset; mDividerBounds.right = rootBounds.width() / 2 + dividerOffset; mBounds1.left = rootBounds.width() / 2 + boundsOffset; @@ -193,6 +176,10 @@ final class AppPairLayout { } } + private boolean isLandscape(Rect bounds) { + return bounds.width() > bounds.height(); + } + Rect mDividerBounds; Rect mBounds1; Rect mBounds2; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java index af06764145e3..ef3e3e0220e7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java @@ -36,6 +36,4 @@ public interface AppPairs { void dump(@NonNull PrintWriter pw, String prefix); /** Called when the shell organizer has been registered. */ void onOrganizerRegistered(); - /** Called when the visibility of the keyguard changes. */ - void onKeyguardVisibilityChanged(boolean showing); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java index 925a4f36d5e6..f2f09820639a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java @@ -16,8 +16,6 @@ package com.android.wm.shell.apppairs; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; - import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager; @@ -30,15 +28,13 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerCallback; -import com.android.wm.shell.common.TaskStackListenerImpl; import java.io.PrintWriter; /** * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}. */ -public class AppPairsController implements AppPairs, TaskStackListenerCallback { +public class AppPairsController implements AppPairs { private static final String TAG = AppPairsController.class.getSimpleName(); private final ShellTaskOrganizer mTaskOrganizer; @@ -48,14 +44,12 @@ public class AppPairsController implements AppPairs, TaskStackListenerCallback { // Active app-pairs mapped by root task id key. private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); private final DisplayController mDisplayController; - private int mForegroundTaskId = INVALID_TASK_ID; public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController, TaskStackListenerImpl taskStackListener) { + DisplayController displayController) { mTaskOrganizer = organizer; mSyncQueue = syncQueue; mDisplayController = displayController; - taskStackListener.addListener(this); } @Override @@ -71,27 +65,6 @@ public class AppPairsController implements AppPairs, TaskStackListenerCallback { } @Override - public void onTaskMovedToFront(int taskId) { - mForegroundTaskId = INVALID_TASK_ID; - for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) { - final AppPair candidate = mActiveAppPairs.valueAt(i); - final boolean containForegroundTask = candidate.contains(taskId); - candidate.setVisible(containForegroundTask); - if (containForegroundTask) { - mForegroundTaskId = candidate.getRootTaskId(); - } - } - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - if (mForegroundTaskId == INVALID_TASK_ID) { - return; - } - mActiveAppPairs.get(mForegroundTaskId).setVisible(!showing); - } - - @Override public boolean pair(int taskId1, int taskId2) { final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1); final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 8a547b4477fd..7b3b5dbfa51c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -18,12 +18,10 @@ package com.android.wm.shell.draganddrop; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS; import static android.content.ClipDescription.EXTRA_PENDING_INTENT; -import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static android.content.Intent.EXTRA_PACKAGE_NAME; @@ -78,7 +76,7 @@ public class DragAndDropPolicy { private static final String TAG = DragAndDropPolicy.class.getSimpleName(); private final Context mContext; - private final IActivityTaskManager mIActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; private final Starter mStarter; private final SplitScreen mSplitScreen; private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>(); @@ -86,15 +84,15 @@ public class DragAndDropPolicy { private DragSession mSession; public DragAndDropPolicy(Context context, SplitScreen splitScreen) { - this(context, ActivityTaskManager.getService(), splitScreen, + this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context, splitScreen)); } @VisibleForTesting - DragAndDropPolicy(Context context, IActivityTaskManager activityTaskManager, + DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager, SplitScreen splitScreen, Starter starter) { mContext = context; - mIActivityTaskManager = activityTaskManager; + mActivityTaskManager = activityTaskManager; mSplitScreen = splitScreen; mStarter = starter; } @@ -103,7 +101,7 @@ public class DragAndDropPolicy { * Starts a new drag session with the given initial drag data. */ void start(DisplayLayout displayLayout, ClipData data) { - mSession = new DragSession(mContext, mIActivityTaskManager, displayLayout, data); + mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data); // TODO(b/169894807): Also update the session data with task stack changes mSession.update(); } @@ -271,7 +269,7 @@ public class DragAndDropPolicy { */ private static class DragSession { private final Context mContext; - private final IActivityTaskManager mIActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; private final ClipData mInitialDragData; final DisplayLayout displayLayout; @@ -285,10 +283,10 @@ public class DragAndDropPolicy { boolean dragItemSupportsSplitscreen; boolean isPhone; - DragSession(Context context, IActivityTaskManager activityTaskManager, + DragSession(Context context, ActivityTaskManager activityTaskManager, DisplayLayout dispLayout, ClipData data) { mContext = context; - mIActivityTaskManager = activityTaskManager; + mActivityTaskManager = activityTaskManager; mInitialDragData = data; displayLayout = dispLayout; } @@ -298,19 +296,14 @@ public class DragAndDropPolicy { */ void update() { - try { - List<ActivityManager.RunningTaskInfo> tasks = - mIActivityTaskManager.getFilteredTasks(1, - false /* filterOnlyVisibleRecents */); - if (!tasks.isEmpty()) { - final ActivityManager.RunningTaskInfo task = tasks.get(0); - runningTaskWinMode = task.getWindowingMode(); - runningTaskActType = task.getActivityType(); - runningTaskId = task.taskId; - runningTaskIsResizeable = task.isResizeable; - } - } catch (RemoteException e) { - // Fall through + List<ActivityManager.RunningTaskInfo> tasks = + mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */); + if (!tasks.isEmpty()) { + final ActivityManager.RunningTaskInfo task = tasks.get(0); + runningTaskWinMode = task.getWindowingMode(); + runningTaskActType = task.getActivityType(); + runningTaskId = task.taskId; + runningTaskIsResizeable = task.isResizeable; } final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 090d2270817b..4e62ea6e7233 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -272,8 +272,9 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { @VisibleForTesting void applyBoundsAndOffsets(WindowContainerToken token, SurfaceControl leash, WindowContainerTransaction wct, SurfaceControl.Transaction t) { - wct.setBounds(token, mCurrentDisplayBounds.isEmpty() ? null : mCurrentDisplayBounds); + wct.setBounds(token, mCurrentDisplayBounds); t.setPosition(leash, mOffsetX, mOffsetY); + t.setWindowCrop(leash, mCurrentDisplayBounds.width(), mCurrentDisplayBounds.height()); } @VisibleForTesting diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java index 061d3f86b669..6e87f131ed95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java @@ -107,6 +107,7 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { transaction.setWindowCrop(leash, crop); } + // TODO(b/173440321): Correct presentation of letterboxed activities in One-handed mode. private void resolveTaskPositionAndCrop( ActivityManager.RunningTaskInfo taskInfo, Point positionInParent, @@ -125,15 +126,18 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { final Rect activityBounds = taskInfo.letterboxActivityBounds; Insets insets = getInsets(); + Rect displayBoundsWithInsets = + new Rect(mWindowManager.getMaximumWindowMetrics().getBounds()); + displayBoundsWithInsets.inset(insets); Rect taskBoundsWithInsets = new Rect(taskBounds); - applyInsets(taskBoundsWithInsets, insets, taskInfo.parentBounds); + taskBoundsWithInsets.intersect(displayBoundsWithInsets); Rect activityBoundsWithInsets = new Rect(activityBounds); - applyInsets(activityBoundsWithInsets, insets, taskInfo.parentBounds); + activityBoundsWithInsets.intersect(displayBoundsWithInsets); Rect parentBoundsWithInsets = new Rect(parentBounds); - applyInsets(parentBoundsWithInsets, insets, parentBounds); + parentBoundsWithInsets.intersect(displayBoundsWithInsets); // Crop need to be in the task coordinates. crop.set(activityBoundsWithInsets); @@ -217,10 +221,4 @@ public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { | WindowInsets.Type.displayCutout()); } - private void applyInsets(Rect innerBounds, Insets insets, Rect outerBounds) { - Rect outerBoundsWithInsets = new Rect(outerBounds); - outerBoundsWithInsets.inset(insets); - innerBounds.intersect(outerBoundsWithInsets); - } - } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java index 56e97b91c9d2..a99ef6342c0d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java @@ -36,7 +36,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Rect; import android.os.Debug; import android.os.Handler; @@ -59,13 +58,14 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Manages the picture-in-picture (PIP) UI and states. */ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback { - private static final String TAG = "PipController"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final String TAG = "TvPipController"; + static final boolean DEBUG = false; /** * Unknown or invalid state @@ -117,9 +117,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private int mResumeResizePinnedStackRunnableState = STATE_NO_PIP; private final Handler mHandler = new Handler(); private List<Listener> mListeners = new ArrayList<>(); - private Rect mPipBounds; - private Rect mDefaultPipBounds = new Rect(); - private Rect mMenuModePipBounds; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; private int mPipTaskId = TASK_ID_NO_PIP; private int mPinnedStackId = INVALID_STACK_ID; @@ -187,11 +184,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (mImeVisible != imeVisible) { if (imeVisible) { // Save the IME height adjustment, and offset to not occlude the IME - mPipBounds.offset(0, -imeHeight); + mPipBoundsState.getNormalBounds().offset(0, -imeHeight); mImeHeightAdjustment = imeHeight; } else { // Apply the inverse adjustment when the IME is hidden - mPipBounds.offset(0, mImeHeightAdjustment); + mPipBoundsState.getNormalBounds().offset(0, mImeHeightAdjustment); } mImeVisible = imeVisible; resizePinnedStack(STATE_PIP); @@ -206,10 +203,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo()); mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds); - mPipBounds.set(mPipBoundsAlgorithm.getNormalBounds()); - if (mDefaultPipBounds.isEmpty()) { - mDefaultPipBounds.set(mPipBoundsAlgorithm.getDefaultBounds()); - } }); } @@ -302,14 +295,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac return; } - Resources res = mContext.getResources(); - mMenuModePipBounds = Rect.unflattenFromString(res.getString( - R.string.pip_menu_bounds)); + final Rect menuBounds = Rect.unflattenFromString( + mContext.getResources().getString(R.string.pip_menu_bounds)); + mPipBoundsState.setExpandedBounds(menuBounds); - // Reset the PIP bounds and apply. PIP bounds can be changed by two reasons. - // 1. Configuration changed due to the language change (RTL <-> RTL) - // 2. SystemUI restarts after the crash - mPipBounds = mDefaultPipBounds; resizePinnedStack(getPinnedTaskInfo() == null ? STATE_NO_PIP : STATE_PIP); } @@ -379,16 +368,22 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } private void onActivityPinned(String packageName) { - if (DEBUG) Log.d(TAG, "onActivityPinned()"); - - RootTaskInfo taskInfo = getPinnedTaskInfo(); + final RootTaskInfo taskInfo = getPinnedTaskInfo(); + if (DEBUG) Log.d(TAG, "onActivityPinned, task=" + taskInfo); if (taskInfo == null) { Log.w(TAG, "Cannot find pinned stack"); return; } - if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo); + + // At this point PipBoundsState knows the correct aspect ratio for this pinned task, so we + // use PipBoundsAlgorithm to calculate the normal bounds for the task (PipBoundsAlgorithm + // will query PipBoundsState for the aspect ratio) and pass the bounds over to the + // PipBoundsState. + mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds()); + mPinnedStackId = taskInfo.taskId; mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; + // Set state to STATE_PIP so we show it when the pinned stack animation ends. mState = STATE_PIP; mPipMediaController.onActivityPinned(); @@ -434,8 +429,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } if (getState() == STATE_PIP) { - if (mPipBounds != mDefaultPipBounds) { - mPipBounds = mDefaultPipBounds; + if (!Objects.equals(mPipBoundsState.getBounds(), mPipBoundsState.getNormalBounds())) { resizePinnedStack(STATE_PIP); } } @@ -509,11 +503,11 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } break; case STATE_PIP_MENU: - newBounds = mMenuModePipBounds; + newBounds = mPipBoundsState.getExpandedBounds(); break; case STATE_PIP: // fallthrough default: - newBounds = mPipBounds; + newBounds = mPipBoundsState.getNormalBounds(); break; } if (newBounds != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 07af289c4f35..6d6c76139c87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -506,7 +506,7 @@ public class SplitScreenController implements SplitScreen, // Try fetching the top running task. final List<RunningTaskInfo> runningTasks = - ActivityTaskManager.getService().getTasks(1 /* maxNum */); + ActivityTaskManager.getInstance().getTasks(1 /* maxNum */); if (runningTasks == null || runningTasks.isEmpty()) { return false; } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt index 2fc6944a3a5f..ced99de21a46 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.apppairs +import android.platform.test.annotations.Presubmit import android.os.SystemClock import android.util.Log import android.view.Surface @@ -43,6 +44,7 @@ import java.io.IOException * Test AppPairs launch. * To run this test: `atest WMShellFlickerTests:AppPairsTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt index 6c4e65818e49..6b44ce6ace0c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -36,6 +37,7 @@ import org.junit.runners.Parameterized * Test Pip launch. * To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index a0056dfc0948..c61a0f171714 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.splitscreen +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -38,6 +39,7 @@ import org.junit.runners.Parameterized * Test SplitScreen launch. * To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt index 32e112dbd598..bf9286980b9a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.splitscreen +import android.platform.test.annotations.Presubmit import android.util.Rational import android.view.Surface import androidx.test.filters.FlakyTest @@ -40,6 +41,7 @@ import org.junit.runners.Parameterized * Test exit SplitScreen mode. * To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest` */ +@Presubmit @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt index 1e328a8dae40..c85561d96091 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -17,7 +17,7 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.Presubmit -import androidx.test.filters.FlakyTest +import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.DOCKED_STACK_DIVIDER @@ -54,7 +54,6 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 161435597) class OpenAppToSplitScreenTest( testName: String, flickerSpec: Flicker @@ -67,7 +66,8 @@ class OpenAppToSplitScreenTest( val testApp = StandardAppHelper(instrumentation, "com.android.wm.shell.flicker.testapp", "SimpleApp") - return FlickerTestRunnerFactory(instrumentation) + // b/161435597 causes the test not to work on 90 degrees + return FlickerTestRunnerFactory(instrumentation, listOf(Surface.ROTATION_0)) .buildTest { configuration -> withTestName { buildTestTag("appToSplitScreen", testApp, configuration) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt new file mode 100644 index 000000000000..d2371bd766f5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import androidx.test.filters.FlakyTest +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:SplitScreenRotateOneLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest +class SplitScreenRotateOneLaunchedAppTest( + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", "SimpleApp") + + return FlickerTestRunnerFactory(instrumentation, repetitions = 3) + .buildTest { configuration -> + withTestName { + buildTestTag("splitScreenRotateOneApp", testApp, configuration) + } + repeat { configuration.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.open() + device.launchSplitScreen() + device.waitForIdle() + } + eachRun { + this.setRotation(configuration.startRotation) + } + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + if (device.isInSplitScreen()) { + device.exitSplitScreen() + } + } + } + transitions { + this.setRotation(configuration.endRotation) + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt new file mode 100644 index 000000000000..67346424acd2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import androidx.test.filters.FlakyTest +import android.view.Surface +import androidx.test.filters.RequiresDevice +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.Flicker +import com.android.server.wm.flicker.FlickerTestRunner +import com.android.server.wm.flicker.FlickerTestRunnerFactory +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.helpers.buildTestTag +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.reopenAppFromOverview +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.repetitions +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:SplitScreenRotateTwoLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest +class SplitScreenRotateTwoLaunchedAppTest( + testName: String, + flickerSpec: Flicker +) : FlickerTestRunner(testName, flickerSpec) { + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val instrumentation = InstrumentationRegistry.getInstrumentation() + val testApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", "SimpleApp") + val secondaryApp = StandardAppHelper(instrumentation, + "com.android.wm.shell.flicker.testapp", + "SplitScreenSecondaryApp") + + return FlickerTestRunnerFactory(instrumentation, repetitions = 3) + .buildTest { configuration -> + withTestName { + buildTestTag("splitScreenRotateTwoApps", testApp, configuration) + } + repeat { configuration.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + testApp.open() + device.pressHome() + secondaryApp.open() + device.pressHome() + device.launchSplitScreen() + device.reopenAppFromOverview() + device.waitForIdle() + } + eachRun { + this.setRotation(configuration.startRotation) + } + } + teardown { + eachRun { + setRotation(Surface.ROTATION_0) + } + test { + testApp.exit() + secondaryApp.exit() + if (device.isInSplitScreen()) { + device.exitSplitScreen() + } + } + } + transitions { + this.setRotation(configuration.endRotation) + } + } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java new file mode 100644 index 000000000000..c9d32c4b1f76 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairLayoutTests.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.apppairs; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.view.Display; +import android.view.SurfaceControl; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link AppPairLayout} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AppPairLayoutTests extends ShellTestCase { + @Mock SurfaceControl mSurfaceControl; + private Display mDisplay; + private Configuration mConfiguration; + private AppPairLayout mAppPairLayout; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mConfiguration = getConfiguration(false); + mDisplay = mContext.getDisplay(); + mAppPairLayout = new AppPairLayout(mContext, mDisplay, mConfiguration, mSurfaceControl); + } + + @After + @UiThreadTest + public void tearDown() { + mAppPairLayout.release(); + } + + @Test + @UiThreadTest + public void testUpdateConfiguration() { + assertThat(mAppPairLayout.updateConfiguration(getConfiguration(false))).isFalse(); + assertThat(mAppPairLayout.updateConfiguration(getConfiguration(true))).isTrue(); + } + + @Test + @UiThreadTest + public void testInitRelease() { + mAppPairLayout.init(); + assertThat(mAppPairLayout.getDividerLeash()).isNotNull(); + mAppPairLayout.release(); + assertThat(mAppPairLayout.getDividerLeash()).isNull(); + } + + private static Configuration getConfiguration(boolean isLandscape) { + final Configuration configuration = new Configuration(); + configuration.unset(); + configuration.orientation = isLandscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; + configuration.windowConfiguration.setBounds( + new Rect(0, 0, isLandscape ? 2160 : 1080, isLandscape ? 1080 : 2160)); + return configuration; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java index 754f73246c86..f12648a7f709 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java @@ -34,7 +34,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.After; import org.junit.Before; @@ -52,7 +51,6 @@ public class AppPairTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; - @Mock private TaskStackListenerImpl mTaskStackListener; @Before public void setUp() { @@ -60,8 +58,7 @@ public class AppPairTests extends ShellTestCase { mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, - mDisplayController, - mTaskStackListener); + mDisplayController); when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); when(mDisplayController.getDisplay(anyInt())).thenReturn( mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java index 6d441ab898ec..f8c68d2018da 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java @@ -34,7 +34,6 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.After; import org.junit.Before; @@ -52,7 +51,6 @@ public class AppPairsControllerTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; - @Mock private TaskStackListenerImpl mTaskStackListener; @Before public void setUp() { @@ -60,8 +58,7 @@ public class AppPairsControllerTests extends ShellTestCase { mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, - mDisplayController, - mTaskStackListener); + mDisplayController); mPool = mController.getPool(); when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); when(mDisplayController.getDisplay(anyInt())).thenReturn( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java index d3dbbfe37985..8ece913de53f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java @@ -24,7 +24,6 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerImpl; import org.junit.After; import org.junit.Before; @@ -42,7 +41,6 @@ public class AppPairsPoolTests { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; - @Mock private TaskStackListenerImpl mTaskStackListener; @Before public void setUp() { @@ -50,8 +48,7 @@ public class AppPairsPoolTests { mController = new TestAppPairsController( mTaskOrganizer, mSyncQueue, - mDisplayController, - mTaskStackListener); + mDisplayController); mPool = mController.getPool(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java index e61cc91c394b..be0963628933 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java @@ -19,14 +19,13 @@ package com.android.wm.shell.apppairs; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TaskStackListenerImpl; public class TestAppPairsController extends AppPairsController { TestAppPairsPool mPool; public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController, TaskStackListenerImpl taskStackListener) { - super(organizer, syncQueue, displayController, taskStackListener); + DisplayController displayController) { + super(organizer, syncQueue, displayController); mPool = new TestAppPairsPool(this); setPairsPool(mPool); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index fad1f057267a..92d4bee7cfc2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -42,7 +42,7 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import android.app.ActivityManager; -import android.app.IActivityTaskManager; +import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; @@ -69,7 +69,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; @@ -88,7 +87,7 @@ public class DragAndDropPolicyTest { private Context mContext; @Mock - private IActivityTaskManager mIActivityTaskManager; + private ActivityTaskManager mActivityTaskManager; @Mock private SplitScreen mSplitScreen; @@ -134,7 +133,7 @@ public class DragAndDropPolicyTest { return null; }).when(mSplitScreen).registerInSplitScreenListener(any()); - mPolicy = new DragAndDropPolicy(mContext, mIActivityTaskManager, mSplitScreen, mStarter); + mPolicy = new DragAndDropPolicy(mContext, mActivityTaskManager, mSplitScreen, mStarter); mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); setClipDataResizeable(mNonResizeableActivityClipData, false); @@ -188,9 +187,9 @@ public class DragAndDropPolicyTest { return info; } - private void setRunningTask(ActivityManager.RunningTaskInfo task) throws RemoteException { - doReturn(Collections.singletonList(task)).when(mIActivityTaskManager) - .getFilteredTasks(anyInt(), anyBoolean()); + private void setRunningTask(ActivityManager.RunningTaskInfo task) { + doReturn(Collections.singletonList(task)).when(mActivityTaskManager) + .getTasks(anyInt(), anyBoolean()); } private void setClipDataResizeable(ClipData data, boolean resizeable) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java index 0f719afd08b3..fc0e20b553d3 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java @@ -96,6 +96,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(75, 0, 125, 75), /* taskBounds */ new Rect(50, 0, 125, 100)), @@ -109,6 +110,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskInfoChanged( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), // Activity is offset by 25 to the left /* activityBounds */ new Rect(50, 0, 100, 75), @@ -130,6 +132,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(150, 0, 200, 75), /* taskBounds */ new Rect(125, 0, 200, 100)), @@ -150,6 +153,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(150, 0, 200, 75), /* taskBounds */ new Rect(125, 0, 200, 100)), @@ -170,6 +174,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 100), /* activityBounds */ new Rect(50, 0, 100, 75), /* taskBounds */ new Rect(25, 0, 100, 100)), @@ -190,6 +195,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -210,6 +216,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -230,6 +237,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 100, 150), /* activityBounds */ new Rect(0, 75, 50, 125), /* taskBounds */ new Rect(0, 50, 100, 125)), @@ -250,6 +258,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds /* parentBounds */ new Rect(0, 0, 200, 125), /* activityBounds */ new Rect(15, 0, 175, 120), /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds @@ -272,6 +281,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { mLetterboxTaskListener.onTaskAppeared( createTaskInfo( /* taskId */ 1, + /* maxBounds= */ new Rect(0, 0, 100, 150), /* parentBounds */ new Rect(0, 75, 100, 225), /* activityBounds */ new Rect(25, 75, 75, 125), /* taskBounds */ new Rect(0, 75, 100, 125)), @@ -285,7 +295,7 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() { setWindowBoundsAndInsets(new Rect(), Insets.NONE); RunningTaskInfo taskInfo = - createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect()); + createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect(), new Rect()); mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); } @@ -306,11 +316,13 @@ public final class LetterboxTaskListenerTest extends ShellTestCase { private static RunningTaskInfo createTaskInfo( int taskId, + final Rect maxBounds, final Rect parentBounds, final Rect activityBounds, final Rect taskBounds) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds); taskInfo.parentBounds = parentBounds; taskInfo.configuration.windowConfiguration.setBounds(taskBounds); taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 4ed5457a2a7f..cd53217d2924 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -429,6 +429,7 @@ cc_defaults { whole_static_libs: ["libskia"], srcs: [ + "canvas/CanvasFrontend.cpp", "canvas/CanvasOpBuffer.cpp", "canvas/CanvasOpRasterizer.cpp", "pipeline/skia/SkiaDisplayList.cpp", @@ -607,6 +608,7 @@ cc_test { "tests/unit/CacheManagerTests.cpp", "tests/unit/CanvasContextTests.cpp", "tests/unit/CanvasOpTests.cpp", + "tests/unit/CanvasFrontendTests.cpp", "tests/unit/CommonPoolTests.cpp", "tests/unit/DamageAccumulatorTests.cpp", "tests/unit/DeferredLayerUpdaterTests.cpp", diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index 49817925d9b4..c6c4ba8a6493 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -19,7 +19,6 @@ X(Save) X(Restore) X(SaveLayer) X(SaveBehind) -X(Concat44) X(Concat) X(SetMatrix) X(Scale) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 473dc53dc4bf..a495ec4ac411 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -125,24 +125,18 @@ struct SaveBehind final : Op { } }; -struct Concat44 final : Op { - static const auto kType = Type::Concat44; - Concat44(const SkM44& m) : matrix(m) {} - SkM44 matrix; - void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } -}; struct Concat final : Op { static const auto kType = Type::Concat; - Concat(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + Concat(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); } }; struct SetMatrix final : Op { static const auto kType = Type::SetMatrix; - SetMatrix(const SkMatrix& matrix) : matrix(matrix) {} - SkMatrix matrix; + SetMatrix(const SkM44& matrix) : matrix(matrix) {} + SkM44 matrix; void draw(SkCanvas* c, const SkMatrix& original) const { - c->setMatrix(SkMatrix::Concat(original, matrix)); + c->setMatrix(SkM44(original) * matrix); } }; struct Scale final : Op { @@ -569,12 +563,9 @@ void DisplayListData::saveBehind(const SkRect* subset) { } void DisplayListData::concat(const SkM44& m) { - this->push<Concat44>(0, m); -} -void DisplayListData::concat(const SkMatrix& matrix) { - this->push<Concat>(0, matrix); + this->push<Concat>(0, m); } -void DisplayListData::setMatrix(const SkMatrix& matrix) { +void DisplayListData::setMatrix(const SkM44& matrix) { this->push<SetMatrix>(0, matrix); } void DisplayListData::scale(SkScalar sx, SkScalar sy) { @@ -834,10 +825,7 @@ bool RecordingCanvas::onDoSaveBehind(const SkRect* subset) { void RecordingCanvas::didConcat44(const SkM44& m) { fDL->concat(m); } -void RecordingCanvas::didConcat(const SkMatrix& matrix) { - fDL->concat(matrix); -} -void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) { +void RecordingCanvas::didSetM44(const SkM44& matrix) { fDL->setMatrix(matrix); } void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) { diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 63d120c4ca19..4851148cd4d8 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -82,8 +82,7 @@ private: void restore(); void concat(const SkM44&); - void concat(const SkMatrix&); - void setMatrix(const SkMatrix&); + void setMatrix(const SkM44&); void scale(SkScalar, SkScalar); void translate(SkScalar, SkScalar); void translateZ(SkScalar); @@ -154,8 +153,7 @@ public: void onFlush() override; void didConcat44(const SkM44&) override; - void didConcat(const SkMatrix&) override; - void didSetMatrix(const SkMatrix&) override; + void didSetM44(const SkM44&) override; void didScale(SkScalar, SkScalar) override; void didTranslate(SkScalar, SkScalar) override; diff --git a/libs/hwui/SaveFlags.h b/libs/hwui/SaveFlags.h new file mode 100644 index 000000000000..f3579a8e9f19 --- /dev/null +++ b/libs/hwui/SaveFlags.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <inttypes.h> + +// TODO: Move this to an enum class +namespace android::SaveFlags { + +// These must match the corresponding Canvas API constants. +enum { + Matrix = 0x01, + Clip = 0x02, + HasAlphaLayer = 0x04, + ClipToLayer = 0x10, + + // Helper constant + MatrixClip = Matrix | Clip, +}; +typedef uint32_t Flags; + +} // namespace android::SaveFlags diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index cd908354aea5..6030c36add7a 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -505,13 +505,11 @@ void Tree::draw(SkCanvas* canvas, const SkRect& bounds, const SkPaint& inPaint) SkPaint paint = inPaint; paint.setAlpha(mProperties.getRootAlpha() * 255); - Bitmap& bitmap = getBitmapUpdateIfDirty(); - SkBitmap skiaBitmap; - bitmap.getSkBitmap(&skiaBitmap); + sk_sp<SkImage> cachedBitmap = getBitmapUpdateIfDirty().makeImage(); int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth()); int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight()); - canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, + canvas->drawImageRect(cachedBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds, &paint, SkCanvas::kFast_SrcRectConstraint); } diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp new file mode 100644 index 000000000000..2c839b0ffc15 --- /dev/null +++ b/libs/hwui/canvas/CanvasFrontend.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanvasFrontend.h" +#include "CanvasOps.h" +#include "CanvasOpBuffer.h" + +namespace android::uirenderer { + +CanvasStateHelper::CanvasStateHelper(int width, int height) { + mInitialBounds = SkIRect::MakeWH(width, height); + mSaveStack.emplace_back(); + mClipStack.emplace_back().setRect(mInitialBounds); + mTransformStack.emplace_back(); + mCurrentClipIndex = 0; + mCurrentTransformIndex = 0; +} + +bool CanvasStateHelper::internalSave(SaveEntry saveEntry) { + mSaveStack.push_back(saveEntry); + if (saveEntry.matrix) { + // We need to push before accessing transform() to ensure the reference doesn't move + // across vector resizes + mTransformStack.emplace_back() = transform(); + mCurrentTransformIndex += 1; + } + if (saveEntry.clip) { + // We need to push before accessing clip() to ensure the reference doesn't move + // across vector resizes + mClipStack.emplace_back() = clip(); + mCurrentClipIndex += 1; + return true; + } + return false; +} + +// Assert that the cast from SkClipOp to SkRegion::Op is valid +static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op); +static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op); +static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op); +static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op); +static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op); +static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op); + +void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) { + clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false); +} + +void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) { + clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true); +} + +bool CanvasStateHelper::internalRestore() { + // Prevent underflows + if (saveCount() <= 1) { + return false; + } + + SaveEntry entry = mSaveStack[mSaveStack.size() - 1]; + mSaveStack.pop_back(); + bool needsRestorePropagation = entry.layer; + if (entry.matrix) { + mTransformStack.pop_back(); + mCurrentTransformIndex -= 1; + } + if (entry.clip) { + // We need to push before accessing clip() to ensure the reference doesn't move + // across vector resizes + mClipStack.pop_back(); + mCurrentClipIndex -= 1; + needsRestorePropagation = true; + } + return needsRestorePropagation; +} + +SkRect CanvasStateHelper::getClipBounds() const { + SkIRect ibounds = clip().getBounds(); + + if (ibounds.isEmpty()) { + return SkRect::MakeEmpty(); + } + + SkMatrix inverse; + // if we can't invert the CTM, we can't return local clip bounds + if (!transform().invert(&inverse)) { + return SkRect::MakeEmpty(); + } + + SkRect ret = SkRect::MakeEmpty(); + inverse.mapRect(&ret, SkRect::Make(ibounds)); + return ret; +} + +bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const { + // TODO: Implement + return false; +} + +bool CanvasStateHelper::quickRejectPath(const SkPath& path) const { + // TODO: Implement + return false; +} + +} // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h new file mode 100644 index 000000000000..5fccccb0bb43 --- /dev/null +++ b/libs/hwui/canvas/CanvasFrontend.h @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +// TODO: Can we get the dependencies scoped down more? +#include "CanvasOps.h" +#include "CanvasOpBuffer.h" +#include <SaveFlags.h> + +#include <SkRasterClip.h> +#include <ui/FatVector.h> + +#include <optional> + +namespace android::uirenderer { + +// Exists to avoid forcing all this common logic into the templated class +class CanvasStateHelper { +protected: + CanvasStateHelper(int width, int height); + ~CanvasStateHelper() = default; + + struct SaveEntry { + bool clip : 1 = false; + bool matrix : 1 = false; + bool layer : 1 = false; + }; + + constexpr SaveEntry saveEntryForLayer() { + return { + .clip = true, + .matrix = true, + .layer = true, + }; + } + + constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) { + return SaveEntry { + .clip = static_cast<bool>(flags & SaveFlags::Clip), + .matrix = static_cast<bool>(flags & SaveFlags::Matrix), + .layer = false + }; + } + + bool internalSave(SaveEntry saveEntry); + bool internalSave(SaveFlags::Flags flags) { + return internalSave(flagsToSaveEntry(flags)); + } + void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) { + internalSave({ + .clip = true, + .matrix = true, + .layer = true + }); + internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect); + } + + bool internalRestore(); + + void internalClipRect(const SkRect& rect, SkClipOp op); + void internalClipPath(const SkPath& path, SkClipOp op); + + SkIRect mInitialBounds; + FatVector<SaveEntry, 6> mSaveStack; + FatVector<SkMatrix, 6> mTransformStack; + FatVector<SkConservativeClip, 6> mClipStack; + + size_t mCurrentTransformIndex; + size_t mCurrentClipIndex; + + const SkConservativeClip& clip() const { + return mClipStack[mCurrentClipIndex]; + } + + SkConservativeClip& clip() { + return mClipStack[mCurrentClipIndex]; + } + +public: + int saveCount() const { return mSaveStack.size(); } + + SkRect getClipBounds() const; + bool quickRejectRect(float left, float top, float right, float bottom) const; + bool quickRejectPath(const SkPath& path) const; + + const SkMatrix& transform() const { + return mTransformStack[mCurrentTransformIndex]; + } + + SkMatrix& transform() { + return mTransformStack[mCurrentTransformIndex]; + } + + // For compat with existing HWUI Canvas interface + void getMatrix(SkMatrix* outMatrix) const { + *outMatrix = transform(); + } + + void setMatrix(const SkMatrix& matrix) { + transform() = matrix; + } + + void concat(const SkMatrix& matrix) { + transform().preConcat(matrix); + } + + void rotate(float degrees) { + SkMatrix m; + m.setRotate(degrees); + concat(m); + } + + void scale(float sx, float sy) { + SkMatrix m; + m.setScale(sx, sy); + concat(m); + } + + void skew(float sx, float sy) { + SkMatrix m; + m.setSkew(sx, sy); + concat(m); + } + + void translate(float dx, float dy) { + transform().preTranslate(dx, dy); + } +}; + +// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream +template <typename CanvasOpReceiver> +class CanvasFrontend final : public CanvasStateHelper { +public: + template<class... Args> + CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height), + mReceiver(std::forward<Args>(args)...) { } + ~CanvasFrontend() = default; + + void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) { + if (internalSave(flagsToSaveEntry(flags))) { + submit<CanvasOpType::Save>({}); + } + } + + void restore() { + if (internalRestore()) { + submit<CanvasOpType::Restore>({}); + } + } + + template <CanvasOpType T> + void draw(CanvasOp<T>&& op) { + // The front-end requires going through certain front-doors, which these aren't. + static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead"); + static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead"); + + if constexpr (T == CanvasOpType::SaveLayer) { + internalSaveLayer(op.saveLayerRec); + } + if constexpr (T == CanvasOpType::SaveBehind) { + // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save + // But we do want to flag it as a layer, such that restore is Definitely Required + internalSave(saveEntryForLayer()); + } + if constexpr (T == CanvasOpType::ClipRect) { + internalClipRect(op.rect, op.op); + } + if constexpr (T == CanvasOpType::ClipPath) { + internalClipPath(op.path, op.op); + } + + submit(std::move(op)); + } + + const CanvasOpReceiver& receiver() const { return mReceiver; } + +private: + CanvasOpReceiver mReceiver; + + template <CanvasOpType T> + void submit(CanvasOp<T>&& op) { + mReceiver.push_container(CanvasOpContainer(std::move(op), transform())); + } +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpRecorder.cpp b/libs/hwui/canvas/CanvasOpRecorder.cpp new file mode 100644 index 000000000000..bb968ee84670 --- /dev/null +++ b/libs/hwui/canvas/CanvasOpRecorder.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "CanvasOpRecorder.h" + +#include "CanvasOpBuffer.h" +#include "CanvasOps.h" + +namespace android::uirenderer {} // namespace android::uirenderer diff --git a/libs/hwui/canvas/CanvasOpRecorder.h b/libs/hwui/canvas/CanvasOpRecorder.h new file mode 100644 index 000000000000..7d95bc4785ea --- /dev/null +++ b/libs/hwui/canvas/CanvasOpRecorder.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "hwui/Canvas.h" +#include "CanvasOpBuffer.h" + +#include <vector> + +namespace android::uirenderer { + +// Interop with existing HWUI Canvas +class CanvasOpRecorder final : /* todo: public Canvas */ { +public: + // Transform ops +private: + struct SaveEntry { + + }; + + std::vector<SaveEntry> mSaveStack; +}; + +} // namespace android::uirenderer diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index f94bae2746d9..4d67166dd8d2 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -18,6 +18,7 @@ #include <cutils/compiler.h> #include <utils/Functor.h> +#include <SaveFlags.h> #include <androidfw/ResourceTypes.h> #include "Properties.h" @@ -57,22 +58,6 @@ class SkiaDisplayList; using DisplayList = skiapipeline::SkiaDisplayList; } -namespace SaveFlags { - -// These must match the corresponding Canvas API constants. -enum { - Matrix = 0x01, - Clip = 0x02, - HasAlphaLayer = 0x04, - ClipToLayer = 0x10, - - // Helper constant - MatrixClip = Matrix | Clip, -}; -typedef uint32_t Flags; - -} // namespace SaveFlags - namespace uirenderer { namespace VectorDrawable { class Tree; diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp new file mode 100644 index 000000000000..05b11795d90d --- /dev/null +++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <canvas/CanvasFrontend.h> +#include <canvas/CanvasOpBuffer.h> +#include <canvas/CanvasOps.h> +#include <canvas/CanvasOpRasterizer.h> + +#include <tests/common/CallCountingCanvas.h> + +#include "SkPictureRecorder.h" +#include "SkColor.h" +#include "SkLatticeIter.h" +#include "pipeline/skia/AnimatedDrawables.h" +#include <SkNoDrawCanvas.h> + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::test; + +class CanvasOpCountingReceiver { +public: + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + mOpCounts[static_cast<size_t>(T)] += 1; + } + + int operator[](CanvasOpType op) const { + return mOpCounts[static_cast<size_t>(op)]; + } + +private: + std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts; +}; + +TEST(CanvasFrontend, saveCount) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.save(); + opCanvas.save(SaveFlags::MatrixClip); + EXPECT_EQ(2, skiaCanvas.getSaveCount()); + EXPECT_EQ(2, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + EXPECT_EQ(1, receiver[CanvasOpType::Save]); + EXPECT_EQ(1, receiver[CanvasOpType::Restore]); +} + +TEST(CanvasFrontend, transform) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + + skiaCanvas.translate(10, 10); + opCanvas.translate(10, 10); + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.scale(2.0f, 1.125f); + opCanvas.scale(2.0f, 1.125f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + skiaCanvas.restore(); + opCanvas.restore(); + } + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.rotate(90.f); + opCanvas.rotate(90.f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + + { + skiaCanvas.save(); + opCanvas.save(SaveFlags::Matrix); + skiaCanvas.skew(5.0f, 2.25f); + opCanvas.skew(5.0f, 2.25f); + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); + skiaCanvas.restore(); + opCanvas.restore(); + } + + skiaCanvas.restore(); + opCanvas.restore(); + } + + EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform()); +} + +TEST(CanvasFrontend, drawOpTransform) { + CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + auto makeDrawRect = [] { + return CanvasOp<CanvasOpType::DrawRect>{ + .rect = SkRect::MakeWH(50, 50), + .paint = SkPaint(SkColors::kBlack), + }; + }; + + opCanvas.draw(makeDrawRect()); + + opCanvas.translate(10, 10); + opCanvas.draw(makeDrawRect()); + + opCanvas.save(); + opCanvas.scale(2.0f, 4.0f); + opCanvas.draw(makeDrawRect()); + opCanvas.restore(); + + opCanvas.save(); + opCanvas.translate(20, 15); + opCanvas.draw(makeDrawRect()); + opCanvas.save(); + opCanvas.rotate(90.f); + opCanvas.draw(makeDrawRect()); + opCanvas.restore(); + opCanvas.restore(); + + // Validate the results + std::vector<SkMatrix> transforms; + transforms.reserve(5); + receiver.for_each([&](auto op) { + // Filter for the DrawRect calls; ignore the save & restores + // (TODO: Add a filtered for_each variant to OpBuffer?) + if (op->type() == CanvasOpType::DrawRect) { + transforms.push_back(op->transform()); + } + }); + + EXPECT_EQ(transforms.size(), 5); + + { + // First result should be identity + const auto& result = transforms[0]; + EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType()); + EXPECT_EQ(SkMatrix::I(), result); + } + + { + // Should be translate 10, 10 + const auto& result = transforms[1]; + EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); + SkMatrix m; + m.setTranslate(10, 10); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + scale 2, 4 + const auto& result = transforms[2]; + EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType()); + SkMatrix m; + m.setTranslate(10, 10); + m.preScale(2.0f, 4.0f); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + translate 20, 15 + const auto& result = transforms[3]; + EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType()); + SkMatrix m; + m.setTranslate(30, 25); + EXPECT_EQ(m, result); + } + + { + // Should be translate 10, 10 + translate 20, 15 + rotate 90 + const auto& result = transforms[4]; + EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask, + result.getType()); + SkMatrix m; + m.setTranslate(30, 25); + m.preRotate(90.f); + EXPECT_EQ(m, result); + } +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp index b15c3221dd60..f186e55ec2e3 100644 --- a/libs/hwui/tests/unit/CanvasOpTests.cpp +++ b/libs/hwui/tests/unit/CanvasOpTests.cpp @@ -16,6 +16,7 @@ #include <gtest/gtest.h> +#include <canvas/CanvasFrontend.h> #include <canvas/CanvasOpBuffer.h> #include <canvas/CanvasOps.h> #include <canvas/CanvasOpRasterizer.h> @@ -26,6 +27,7 @@ #include "SkColor.h" #include "SkLatticeIter.h" #include "pipeline/skia/AnimatedDrawables.h" +#include <SkNoDrawCanvas.h> using namespace android; using namespace android::uirenderer; @@ -78,6 +80,21 @@ struct MockOp<MockTypes::Lifecycle> { using MockBuffer = OpBuffer<MockTypes, MockOpContainer>; +class CanvasOpCountingReceiver { +public: + template <CanvasOpType T> + void push_container(CanvasOpContainer<T>&& op) { + mOpCounts[static_cast<size_t>(T)] += 1; + } + + int operator[](CanvasOpType op) const { + return mOpCounts[static_cast<size_t>(op)]; + } + +private: + std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts; +}; + template<typename T> static int countItems(const T& t) { int count = 0; @@ -614,4 +631,35 @@ TEST(CanvasOp, immediateRendering) { rasterizer.draw(op); EXPECT_EQ(1, canvas->drawRectCount); EXPECT_EQ(1, canvas->sumTotalDrawCalls()); +} + +TEST(CanvasOp, frontendSaveCount) { + SkNoDrawCanvas skiaCanvas(100, 100); + CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100); + const auto& receiver = opCanvas.receiver(); + + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.save(); + opCanvas.save(SaveFlags::MatrixClip); + EXPECT_EQ(2, skiaCanvas.getSaveCount()); + EXPECT_EQ(2, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + skiaCanvas.restore(); + opCanvas.restore(); + EXPECT_EQ(1, skiaCanvas.getSaveCount()); + EXPECT_EQ(1, opCanvas.saveCount()); + + EXPECT_EQ(1, receiver[Op::Save]); + EXPECT_EQ(1, receiver[Op::Restore]); +} + +TEST(CanvasOp, frontendTransform) { + }
\ No newline at end of file diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp index da6a2604a4b6..bffdeca4db54 100644 --- a/libs/hwui/tests/unit/CommonPoolTests.cpp +++ b/libs/hwui/tests/unit/CommonPoolTests.cpp @@ -54,7 +54,9 @@ TEST(DISABLED_CommonPool, threadCount) { EXPECT_EQ(0, threads.count(gettid())); } -TEST(CommonPool, singleThread) { +// Disabled since this is flaky. This isn't a necessarily useful functional test, so being +// disabled isn't that significant. However it may be good to resurrect this somehow. +TEST(CommonPool, DISABLED_singleThread) { std::mutex mutex; std::condition_variable fence; bool isProcessing = false; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index accf0f4f2aaa..26bc65915c7a 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -1107,26 +1107,26 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { EXPECT_EQ(dy, TRANSLATE_Y); } - virtual void didSetMatrix(const SkMatrix& matrix) override { + virtual void didSetM44(const SkM44& matrix) override { mDrawCounter++; // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_TRUE(matrix.isIdentity()); + EXPECT_TRUE(matrix == SkM44()); EXPECT_TRUE(getTotalMatrix().isIdentity()); } - virtual void didConcat(const SkMatrix& matrix) override { + virtual void didConcat44(const SkM44& matrix) override { mDrawCounter++; if (mFirstDidConcat) { // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix. mFirstDidConcat = false; - EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), + EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), matrix); EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y), getTotalMatrix()); } else { // Second invocation is preparing the matrix for an elevated RenderNodeDrawable. - EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), matrix); + EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix); EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix()); } } diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index c19e1ed6ce75..4659a929a9eb 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -264,6 +264,8 @@ TEST(RenderNode, releasedCallback) { TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) { TestUtils::syncHierarchyPropertiesAndDisplayList(node); }); + // Fence on any remaining post'd work + TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {}); EXPECT_EQ(2, counts.sync); EXPECT_EQ(1, counts.destroyed); } diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl index fcb7d608f662..47debe90c854 100644 --- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl +++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl @@ -23,5 +23,6 @@ import android.media.metrics.PlaybackMetrics; * @hide */ interface IPlaybackMetricsManager { - void reportPlaybackMetrics(in PlaybackMetrics metrics, int userId); + void reportPlaybackMetrics(in String sessionId, in PlaybackMetrics metrics, int userId); + String getSessionId(int userId); }
\ No newline at end of file diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java index 3606f53d7220..d51ff473696d 100644 --- a/media/java/android/media/metrics/PlaybackMetricsManager.java +++ b/media/java/android/media/metrics/PlaybackMetricsManager.java @@ -16,6 +16,7 @@ package android.media.metrics; +import android.annotation.NonNull; import android.os.RemoteException; /** @@ -38,10 +39,24 @@ public class PlaybackMetricsManager { /** * Reports playback metrics. + * @hide + */ + public void reportPlaybackMetrics(@NonNull String sessionId, PlaybackMetrics metrics) { + try { + mService.reportPlaybackMetrics(sessionId, metrics, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Creates a playback session. */ - public void reportPlaybackMetrics(PlaybackMetrics metrics) { + public PlaybackSession createSession() { try { - mService.reportPlaybackMetrics(metrics, mUserId); + String id = mService.getSessionId(mUserId); + PlaybackSession session = new PlaybackSession(id, this); + return session; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java new file mode 100644 index 000000000000..4ad89067952c --- /dev/null +++ b/media/java/android/media/metrics/PlaybackSession.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.metrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.AnnotationValidations; + +import java.util.Objects; + +/** + * @hide + */ +public final class PlaybackSession implements AutoCloseable { + private final @NonNull String mId; + private final @NonNull PlaybackMetricsManager mManager; + private boolean mClosed = false; + + /** + * Creates a new PlaybackSession. + * + * @hide + */ + public PlaybackSession(@NonNull String id, @NonNull PlaybackMetricsManager manager) { + mId = id; + mManager = manager; + AnnotationValidations.validate(NonNull.class, null, mId); + AnnotationValidations.validate(NonNull.class, null, mManager); + } + + /** + * Reports playback metrics. + */ + public void reportPlaybackMetrics(@NonNull PlaybackMetrics metrics) { + mManager.reportPlaybackMetrics(mId, metrics); + } + + public @NonNull String getId() { + return mId; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PlaybackSession that = (PlaybackSession) o; + return Objects.equals(mId, that.mId); + } + + @Override + public int hashCode() { + return Objects.hash(mId); + } + + @Override + public void close() throws Exception { + mClosed = true; + } +} diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index a9da77230214..c2613716cd07 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -308,6 +308,7 @@ public class Tuner implements AutoCloseable { .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId, FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN); } + releaseAll(); mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST)); } }; @@ -610,7 +611,6 @@ public class Tuner implements AutoCloseable { break; } case MSG_RESOURCE_LOST: { - releaseAll(); if (mOnResourceLostListener != null && mOnResourceLostListenerExecutor != null) { mOnResourceLostListenerExecutor.execute( diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java index a9c754d3df79..c6c7142c3bd0 100755 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java @@ -410,7 +410,7 @@ public class InstallInstalling extends AlertActivity { InstallInstalling.this, mInstallId, broadcastIntent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java index caf971800ad2..eea12eca1859 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java @@ -243,8 +243,8 @@ class PackageInstalledNotificationUtils { } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(mContext, - 0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(mContext, 0 /* request code */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** @@ -260,8 +260,8 @@ class PackageInstalledNotificationUtils { } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(mContext, - 0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(mContext, 0 /* request code */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } /** diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java index c4dceb4fe079..7bf27dfe6420 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java @@ -95,7 +95,8 @@ public class UninstallUninstalling extends Activity implements broadcastIntent.setPackage(getPackageName()); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId, - broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); + broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_MUTABLE); int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0; flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java index bf4b03c56b79..7e0490a233f9 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java @@ -269,7 +269,8 @@ public class PackageInstallerImpl { // Create a matching PendingIntent and use it to generate the IntentSender Intent broadcastIntent = new Intent(action); PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(), - broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_MUTABLE); return pendingIntent.getIntentSender(); } diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 2b30e0a61863..41af185da0b7 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery word tydelik beperk"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 0bebfabf2eb9..8e4e402c2e16 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪ ለጊዜው ተገድቧል"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 6eaf3a98da11..691ec046da02 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - تأثير محدود على البطارية مؤقتًا"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index e207e1ce4c12..61214e09ba26 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সাময়িকভাৱে সীমিত কৰা হৈছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ff1209c754d1..db61527a54d8 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareya müvəqqəti məhdudlaşdırılıb"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index eb099c1926a7..70fb3636b492 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je trenutno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index e6e49ab2063c..8a0db8261a71 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарад акумулятара часова абмежаваны"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 251c4dd545c4..27dcf105fd2e 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Батерията е временно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 6a99a84358d4..29862e6efd31 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -211,9 +211,9 @@ <string name="adb_wireless_error" msgid="721958772149779856">"সমস্যা"</string> <string name="adb_wireless_settings" msgid="2295017847215680229">"ওয়্যারলেস ডিবাগিং"</string> <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"কোন কোন ডিভাইস উপলভ্য আছে তা দেখে নিয়ে ব্যবহার করার জন্য, ওয়্যারলেস ডিবাগিং চালু করুন"</string> - <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR কোড স্ক্যানার ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> - <string name="adb_pair_method_code_title" msgid="1122590300445142904">"যোগ করার কোড ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_pair_method_code_title" msgid="1122590300445142904">"পেয়ারিং কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ছয় সংখ্যার কোড ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string> <string name="adb_paired_devices_title" msgid="5268997341526217362">"যোগ করা ডিভাইস"</string> <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"এখন কানেক্ট রয়েছে"</string> @@ -222,16 +222,16 @@ <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ডিভাইসে আঙ্গুলের ছাপ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string> <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"কানেক্ট করা যায়নি"</string> <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>টি সঠিক নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন"</string> - <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে যোগ করুন"</string> - <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাই যোগ করার কোড"</string> - <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"যোগ করা যায়নি"</string> + <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে পেয়ার করুন"</string> + <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাইয়ের সাথে পেয়ার করার কোড"</string> + <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"পেয়ার করা যায়নি"</string> <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"ডিভাইসটি একই নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন।"</string> - <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ডিভাইস যোগ করা হচ্ছে…"</string> <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ডিভাইস যোগ করা যায়নি। এটি দুটি কারণে হয়ে থাকে - QR কোডটি সঠিক নয় বা ডিভাইসটি একই নেটওয়ার্কে কানেক্ট করা নেই।"</string> <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP অ্যাড্রেস ও পোর্ট"</string> <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR কোড স্ক্যান করুন"</string> - <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাইয়ের সাহায্যে ডিভাইস যোগ করুন"</string> + <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string> <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"একটি ওয়াই-ফাই নেটওয়ার্কের সাথে কানেক্ট করুন"</string> <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string> <string name="bugreport_in_power" msgid="8664089072534638709">"ত্রুটি প্রতিবেদনের শর্টকাট"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারি কিছুক্ষণের জন্য সীমিত করা হয়েছে"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index aeb7f2ecc567..4704ec8db79a 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index df4c99e20505..5d04963d9b4a 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconnectat"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"S\'està desconnectant..."</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"S\'està connectant…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connectat"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"S\'està vinculant..."</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés al telèfon)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense contingut multimèdia)"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporalment"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index e563ca5a03f8..a99a4dabbc60 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterie dočasně omezena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 8a3d054c3ae8..c90155eb2b68 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidigt begrænset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 624b299a96c3..b5b9fc480607 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Zum Wechseln tippen."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Zum Wechseln tippen."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"Standby-Status der App:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"Einstellungen für Medientranscodierung"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodierung deaktivieren"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodierung für Apps aktivieren"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akku vorübergehend eingeschränkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 7ea90506ac06..203ec40c5a42 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η μπαταρία περιορίστηκε προσωρινά."</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 7affb5c89dfc..08ff55099c9c 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -196,7 +196,7 @@ <string name="choose_profile" msgid="343803890897657450">"Elegir perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Personal"</string> <string name="category_work" msgid="4014193632325996115">"Trabajo"</string> - <string name="development_settings_title" msgid="140296922921597393">"Opciones para programadores"</string> + <string name="development_settings_title" msgid="140296922921597393">"Opciones para desarrolladores"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Activar opciones para programador"</string> <string name="development_settings_summary" msgid="8718917813868735095">"Establecer opciones para desarrollar aplicaciones"</string> <string name="development_settings_not_available" msgid="355070198089140951">"Las opciones de programador no están disponibles para este usuario."</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index ccdc1c443fbd..6f6a1a1c9b17 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> hasta cargarse completamente"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta cargarse completamente)"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: batería limitada temporalmente"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 852d6df7d31e..9d6326e45706 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akutase on ajutiselt piiratud"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 6b6b9b63df65..01afa1706e3e 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria mugatuta egongo da aldi batez"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index af1e55be67c5..dd7ab9672e96 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - باتری موقتاً محدود شده است"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 8e819efe3557..b86b02d8c25f 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akun käyttöä rajoitettu tilapäisesti"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 4754d15b5a52..0bca1dba1aa4 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pile limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index a3863f596067..ebde43ba52a2 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterie limitée temporairement"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 81e79e4a27a9..b040817b2801 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> (batería limitada temporalmente)"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 9bf09704a4f5..c1bd7cac30a6 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"નિષ્ક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"સક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ઍપ સ્ટૅન્ડબાયની સ્થિતિ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીનો વપરાશ હંગામી રૂપે મર્યાદિત છે"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 151b861cbd3b..d9153a1ab542 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - कुछ समय के लिए, बैटरी का सीमित इस्तेमाल होगा"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index d37879f6e153..b51e096ca6b7 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 4ac5308474c5..33770fc1bdd8 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésből"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akkumulátor ideiglenesen korlátozva"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index b0734fdee27e..06d70f854f27 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Մարտկոցը ժամանակավորապես սահմանափակված է"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 9db21c796bbf..4123d9e3b755 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Daya baterai terbatas untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index d9ee03adefa4..3b9bef872177 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Rafhlaða takmörkuð tímabundið"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 57378e78af65..1ab6b24456f3 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -66,7 +66,7 @@ <string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string> <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string> <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string> - <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso"</string> + <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string> <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string> <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string> <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index fd53f14bd48f..15868d5fc997 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"אפליקציה לא פעילה. הקש כדי להחליף מצב."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"אפליקציה פעילה. הקש כדי להחליף מצב."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"אפליקציה במצב המתנה:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"הגדרות של המרת קידוד למדיה"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"השבתה של המרת קידוד"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"הפעלה של המרת קידוד לאפליקציות"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"יישום WebView"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - הסוללה מוגבלת באופן זמני"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index c0aa74bafc56..89ee98eeeb42 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池の使用が一時的に制限されています"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 09f3ff5c957a..2bc1c4d20728 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысы уақытша шектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 42e6a53a0078..7306cf8c5d3f 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានដាក់កម្រិតថ្មជាបណ្ដោះអាសន្ន"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"មិនស្គាល់"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូលថ្ម"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 658b987dd833..0c1502ca620d 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 2b0843dcef39..13223440d098 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 일시적으로 배터리 사용 제한"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 499fd88f5a4f..9697aa70e44f 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны колдонуу убактлуу чектелген"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index a5be6017ca62..720122262dcf 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ບໍ່ໄດ້ນຳໃຊ້. ແຕະບໍ່ສັບປ່ຽນ."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ນຳໃຊ້ຢູ່. ແຕະເພື່ອສັບປ່ຽນ."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ສະຖານະສະແຕນບາຍແອັບ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> - <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> - <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈຳກັດແບັດເຕີຣີຊົ່ວຄາວ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 181fb10dd845..e62eeb2dbd97 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akumuliatorius laikinai apribotas"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 717e2b0c89d8..2f305612a5fc 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>, akumulatora uzlāde pagaidām ierobežota"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 2ccb150b8ca3..1936fe2d32a4 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батеријата е привремено ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 768ad5e70698..de4a49a2ed06 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"നിഷ്ക്രിയം. മാറ്റുന്നതിനു ടാപ്പുചെയ്യുക."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"സജീവം. മാറ്റുന്നതിന് ടാപ്പുചെയ്യുക."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ആപ്പ് സ്റ്റാൻഡ്ബൈ നില:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"മീഡിയ ട്രാൻസ്കോഡ് ചെയ്യൽ ക്രമീകരണം"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ട്രാൻസ്കോഡ് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കുക"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ആപ്പുകൾക്കായി ട്രാൻസ്കോഡ് ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി താൽക്കാലം പരിമിതപ്പെടുത്തി"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 61ebca5992b8..dee8add685f9 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг түр хугацаанд хязгаарласан"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index ef319c3a4fc1..60ad68e42c09 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"अॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रान्सकोडिंगची सेटिंग्ज"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिंग बंद करा"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ॲप्ससाठी ट्रान्सकोडिंग सुरू करा"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरी तात्पुरती मर्यादित आहे"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 83c038453265..e4d591207937 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateri terhad untuk sementara"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index f398487856bf..09a8bf87700b 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီ ယာယီကန့်သတ်ထားသည်"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 0ef5809e2a6d..d1af28940f1b 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidig begrenset"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index b956e2104c82..0c6114b9e220 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"मिडिया ट्रान्सकोडिङ सेटिङ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिङ अफ गर्नुहोस्"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"एपहरूमा ट्रान्सकोडिङ अन गर्नुहोस्"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string> <string name="power_charging_duration" msgid="5005740040558984057">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - केही समयका लागि ब्याट्री प्रयोग सीमित गरिएको छ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 3223857a0949..a409756e5b5f 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterij tijdelijk beperkt"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 11bb550caa49..0a0eeff6de60 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ନିଷ୍କ୍ରିୟ। ଟୋଗଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ସକ୍ରିୟ। ବଦଳାଇବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ଆପ୍ ଷ୍ଟାଣ୍ଡବାଏ ଅବସ୍ଥା:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍ଭ୍ୟୁ ପ୍ରୟୋଗ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବ୍ୟାଟେରୀ ଅସ୍ଥାୟୀ ଭାବେ ସୀମିତ ଅଛି"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 8d66f3ca4adc..c9c37d922537 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ਅਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"ਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ਐਪ ਸਟੈਂਡਬਾਈ ਸਥਿਤੀ:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"ਮੀਡੀਆ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਸੈਟਿੰਗਾਂ"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਕੁਝ ਸਮੇਂ ਲਈ ਸੀਮਤ"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index ed96c46848d9..e894c2cbc34c 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – bateria tymczasowo ograniczona"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 46d6d0a89f5e..57a0454f3465 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterie limitată temporar"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 2a79ad3005ff..09b8de04388b 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> • Уровень заряда временно ограничен"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 78f355865ced..d36140f66b54 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය තාවකාලිකව සීමිතයි"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 152c7a885457..096e95e7050b 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batéria je dočasne obmedzená"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index a2f093e89ad8..345fa36d3bd6 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je začasno omejena"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index b4589173f234..3953a6a4f6e0 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateria e kufizuar përkohësisht"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 970c70f48c68..70b287933c93 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – батерија је тренутно ограничена"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 42d907734e30..f466e882c2fc 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – batteriet är tillfälligt begränsat"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index f1a41994d2c8..e1060fec2a66 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Imebakisha <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ijae chaji"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri imedhibitiwa kwa muda"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 2d696c5f79eb..ba98d79ff7e4 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>-பேட்டரி தற்காலிகக் கட்டுப்பாட்டிலுள்ளது"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 8e0f0b603523..0b93979dbd07 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> -బ్యాటరీ తాత్కాలికంగా పరిమితం చేయబడింది"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 81895b923a44..a31e14611bf6 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - การชาร์จแบตเตอรี่จำกัดชั่วคราว"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 5d1630f28675..bbb77790e68a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pansamantalang limitado ang baterya"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 34c96a815616..fba7f41bbbf5 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pil geçici olarak sınırlı"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index de2456cce092..b633752d28a9 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – дані акумулятора тимчасово недоступні"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 7842ba9bcdbf..b4d8ad2576bd 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -399,12 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیر فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"ایپ اسٹینڈ بائی کی حالت:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <!-- no translation found for transcode_settings_title (2581975870429850549) --> - <skip /> - <!-- no translation found for transcode_enable_all (9102460144086871903) --> - <skip /> - <!-- no translation found for transcode_skip_apps (8249721984597390142) --> - <skip /> + <string name="transcode_settings_title" msgid="2581975870429850549">"میڈیا ٹرانسکوڈنگ کی ترتیبات"</string> + <string name="transcode_enable_all" msgid="9102460144086871903">"ٹرانسکوڈنگ غیر فعال کریں"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"ایپس کے لئے ٹرانسکوڈنگ فعال کریں"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView کا نفاذ"</string> @@ -452,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری عارضی طور پر محدود ہے"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 9457d9001a88..5bb422ecf96a 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvat darajasi vaqtincha cheklangan"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 235987c423d5..0acf9418b718 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc đầy"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc đầy"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Thời lượng pin bị hạn chế tạm thời"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 46188e9859e6..ca2ad87fb864 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暂时限用电池"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 6f5c0253e94f..16ca19d313c1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -399,9 +399,9 @@ <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕按即可切換。"</string> <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕按即可切換。"</string> <string name="standby_bucket_summary" msgid="5128193447550429600">"備用應用程式狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string> - <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string> + <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼設定"</string> <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string> - <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string> + <string name="transcode_skip_apps" msgid="8249721984597390142">"為應用程式啟用轉碼功能"</string> <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string> <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string> <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string> @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池充電"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index f966df68e0c3..810119b97777 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池用量"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 8ad37d330bc4..23beeb1aee82 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -449,8 +449,7 @@ <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string> <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string> <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string> - <!-- no translation found for power_charging_limited (5902301801611726210) --> - <skip /> + <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ibhethri ikhawulelwe okwesikhashana"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string> <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 9f16d033aea5..ac20ee14ced2 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -408,7 +408,8 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } /** - * Checks if an admin has enforced minimum password quality requirements on the given user. + * Checks if an admin has enforced minimum password quality or complexity requirements on the + * given user. * * @return EnforcedAdmin Object containing the enforced admin component and admin user details, * or {@code null} if no quality requirements are set. If the requirements are set by @@ -428,6 +429,30 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { } LockPatternUtils lockPatternUtils = new LockPatternUtils(context); + final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId); + if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) { + // First, check if there's a Device Owner. If so, then only it can apply password + // complexity requiremnts (there can be no secondary profiles). + final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser(); + if (deviceOwnerUser != null) { + return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser); + } + + // The complexity could be enforced by a Profile Owner - either in the current user + // or the current user is the parent user that is affected by the profile owner. + for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) { + final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id); + if (profileOwnerComponent != null) { + return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id)); + } + } + + // Should not get here: A Device Owner or Profile Owner should be found. + throw new IllegalStateException( + String.format("Could not find admin enforcing complexity %d for user %d", + aggregatedComplexity, userId)); + } + if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) { // userId is managed profile and has a separate challenge, only consider // the admins in that user. diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 59d8acb82196..8fd1910041c8 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -49,6 +49,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class BluetoothEventManager { private static final String TAG = "BluetoothEventManager"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final LocalBluetoothAdapter mLocalAdapter; private final CachedBluetoothDeviceManager mDeviceManager; @@ -366,6 +367,9 @@ public class BluetoothEventManager { * BluetoothDevice.UNBOND_REASON_* */ private void showUnbondMessage(Context context, String name, int reason) { + if (DEBUG) { + Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason); + } int errorMsg; switch (reason) { @@ -382,6 +386,7 @@ public class BluetoothEventManager { case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: + case BluetoothDevice.UNBOND_REASON_REMOVED: errorMsg = R.string.bluetooth_pairing_error_message; break; default: diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java index 00f94f5c2e64..9d4669a5a37d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java @@ -18,6 +18,7 @@ package com.android.settingslib.media; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.content.Context; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; @@ -56,8 +57,9 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public Drawable getIcon() { - final Drawable drawable = getIconWithoutBackground(); - if (!isFastPairDevice()) { + final Drawable drawable = + BluetoothUtils.getBtDrawableWithDescription(mContext, mCachedDevice).first; + if (!(drawable instanceof BitmapDrawable)) { setColorFilter(drawable); } return BluetoothUtils.buildAdvancedDrawable(mContext, drawable); @@ -65,9 +67,7 @@ public class BluetoothMediaDevice extends MediaDevice { @Override public Drawable getIconWithoutBackground() { - return isFastPairDevice() - ? BluetoothUtils.getBtDrawableWithDescription(mContext, mCachedDevice).first - : mContext.getDrawable(R.drawable.ic_headphone); + return BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first; } @Override diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java index ba1dc64ce1e6..6a4d650ebf31 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java @@ -35,6 +35,8 @@ import android.content.IntentFilter; import android.os.UserHandle; import android.telephony.TelephonyManager; +import com.android.settingslib.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,6 +51,8 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class BluetoothEventManagerTest { + private static final String DEVICE_NAME = "test_device_name"; + @Mock private LocalBluetoothAdapter mLocalAdapter; @Mock @@ -71,6 +75,8 @@ public class BluetoothEventManagerTest { private BluetoothDevice mDevice2; @Mock private LocalBluetoothProfileManager mLocalProfileManager; + @Mock + private BluetoothUtils.ErrorListener mErrorListener; private Context mContext; private Intent mIntent; @@ -92,6 +98,7 @@ public class BluetoothEventManagerTest { mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1); mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2); + BluetoothUtils.setErrorListener(mErrorListener); } @Test @@ -344,4 +351,80 @@ public class BluetoothEventManagerTest { assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse(); assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse(); } + + @Test + public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_error_message)); + } + + @Test + public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, + BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_device_down_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_REJECTED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_rejected_error_message)); + } + + @Test + public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() { + mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); + mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice); + mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE); + mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_FAILED); + when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1); + when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME); + + mContext.sendBroadcast(mIntent); + + verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME), + eq(R.string.bluetooth_pairing_pin_error_message)); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java index 8973d116e438..e887c45083c0 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; +import android.graphics.drawable.BitmapDrawable; import com.android.settingslib.bluetooth.CachedBluetoothDevice; @@ -96,4 +97,17 @@ public class BluetoothMediaDeviceTest { assertThat(mBluetoothMediaDevice.isFastPairDevice()).isFalse(); } + + @Test + public void getIcon_isNotFastPairDevice_drawableTypeIsNotBitmapDrawable() { + final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class); + when(mDevice.getDevice()).thenReturn(bluetoothDevice); + + final String value = "False"; + final byte[] bytes = value.getBytes(); + when(bluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) + .thenReturn(bytes); + + assertThat(mBluetoothMediaDevice.getIcon() instanceof BitmapDrawable).isFalse(); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index b061df1423ba..40b0fcff3aac 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -496,11 +496,14 @@ final class SettingsState { public List<String> setSettingsLocked(String prefix, Map<String, String> keyValues, String packageName) { List<String> changedKeys = new ArrayList<>(); + final Iterator<Map.Entry<String, Setting>> iterator = mSettings.entrySet().iterator(); // Delete old keys with the prefix that are not part of the new set. - for (int i = 0; i < mSettings.keySet().size(); ++i) { - String key = mSettings.keyAt(i); - if (key.startsWith(prefix) && !keyValues.containsKey(key)) { - Setting oldState = mSettings.remove(key); + while (iterator.hasNext()) { + Map.Entry<String, Setting> entry = iterator.next(); + final String key = entry.getKey(); + final Setting oldState = entry.getValue(); + if (key != null && key.startsWith(prefix) && !keyValues.containsKey(key)) { + iterator.remove(); FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java index 5e5a9d9b2ec8..c33f02dff561 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java @@ -82,6 +82,7 @@ public class WifiSoftApConfigChangedNotifier { private static PendingIntent getPendingActivity(Context context) { Intent intent = new Intent("com.android.settings.WIFI_TETHER_SETTINGS") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(context, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index af12ddd8895b..75eea8db8085 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -230,7 +230,7 @@ public class SettingsBackupTest { Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_VR, - Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, + Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH, Settings.Global.DEVICE_DEMO_MODE, Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS, Settings.Global.BATTERY_SAVER_CONSTANTS, @@ -319,7 +319,6 @@ public class SettingsBackupTest { Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST, - Settings.Global.LOCATION_GLOBAL_KILL_SWITCH, Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, Settings.Global.LOCK_SOUND, Settings.Global.LOOPER_STATS, @@ -576,8 +575,6 @@ public class SettingsBackupTest { Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED, Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS, Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS, - Settings.Global.ISOLATED_STORAGE_LOCAL, - Settings.Global.ISOLATED_STORAGE_REMOTE, Settings.Global.APPOP_HISTORY_PARAMETERS, Settings.Global.APPOP_HISTORY_MODE, Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2a699ea45abe..2e3ea24f62e2 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -70,6 +70,7 @@ <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" /> <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" /> <uses-permission android:name="android.permission.DUMP" /> + <uses-permission android:name="android.permission.CONTROL_UI_TRACING" /> <uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" /> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <!-- Internal permissions granted to the shell. --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 7120cc21c821..52b41a43c63e 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -530,6 +530,14 @@ androidprv:alwaysFocusable="true" android:excludeFromRecents="true" /> + <!-- started from TvNotificationPanel --> + <activity + android:name=".statusbar.tv.notifications.TvNotificationPanelActivity" + android:excludeFromRecents="true" + android:launchMode="singleTask" + android:noHistory="true" + android:theme="@style/TvSidePanelTheme" /> + <!-- started from SliceProvider --> <activity android:name=".SlicePermissionActivity" android:theme="@style/Theme.SystemUI.Dialog.Alert" diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 17d2f9c89c30..f884270eaba8 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -2,8 +2,11 @@ set noparent dsandler@android.com +aaliomer@google.com adamcohen@google.com +alexflo@google.com asc@google.com +awickham@google.com beverlyt@google.com brockman@google.com cinek@google.com @@ -11,11 +14,16 @@ cwren@google.com dupin@google.com ethibodeau@google.com evanlaird@google.com +gwasserman@google.com hwwang@google.com hyunyoungs@google.com jaggies@google.com +jamesoleary@google.com +jeffdq@google.com jjaggi@google.com +jonmiranda@google.com joshmcgrath@google.com +joshtrask@google.com juliacr@google.com juliatuttle@google.com kchyn@google.com @@ -24,28 +32,38 @@ kprevas@google.com lynhan@google.com madym@google.com mankoff@google.com +mett@google.com +mkephart@google.com +mpietal@google.com mrcasey@google.com mrenouf@google.com nbenbernou@google.com nesciosquid@google.com ogunwale@google.com peanutbutter@google.com +pinyaoting@google.com pixel@google.com roosa@google.com +santie@google.com snoeberger@google.com +sreyasr@google.com steell@google.com +sfufa@google.com stwu@google.com sunnygoyal@google.com susikp@google.com +thiruram@google.com tracyzhou@google.com tsuji@google.com twickham@google.com +vadimt@google.com +victortulias@google.com winsonc@google.com +xuqiu@google.com zakcohen@google.com #Android Auto hseog@google.com #Android TV -rgl@google.com - +rgl@google.com
\ No newline at end of file diff --git a/packages/SystemUI/res/color/background_protect_secondary.xml b/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml index 97744dbe9190..3345e6e42500 100644 --- a/packages/SystemUI/res/color/background_protect_secondary.xml +++ b/packages/SystemUI/res-keyguard/color/notification_background_dimmed_color.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2017 The Android Open Source Project + ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,7 +14,6 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="?attr/wallpaperTextColorSecondary" /> + <item android:alpha="0.7" android:color="?android:attr/colorBackground" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/circle_white.xml b/packages/SystemUI/res-keyguard/drawable/circle_white.xml new file mode 100644 index 000000000000..d1b20979c29e --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/circle_white.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<shape xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + <solid android:color="#33FFFFFF" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml index cc2089f69287..99c70a54a3cd 100644 --- a/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml +++ b/packages/SystemUI/res-keyguard/drawable/kg_emergency_button_background.xml @@ -19,13 +19,13 @@ <item android:id="@android:id/background"> <shape android:color="@android:color/transparent"> - <stroke android:width="1dp" android:color="?attr/wallpaperTextColorSecondary"/> + <stroke android:width="1dp" android:color="?android:attr/textColorSecondary"/> <corners android:radius="24dp"/> </shape> </item> <item android:id="@android:id/mask"> <shape android:shape="rectangle"> - <solid android:color="?attr/wallpaperTextColorSecondary"/> + <solid android:color="?android:attr/textColorSecondary"/> <corners android:radius="24dp"/> </shape> </item> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index b75c2c4cea6c..c82bda6620bd 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -95,7 +95,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center_horizontal" - android:textSize="170dp" + android:textSize="180dp" android:letterSpacing="0.02" android:lineSpacingMultiplier=".8" android:includeFontPadding="false" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml index b06d6a989cb8..e1550aa0c87c 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -34,7 +34,7 @@ android:layout_weight="7" /> - <!-- Password entry field --> + <!-- Password entry field --> <FrameLayout android:layout_height="wrap_content" android:layout_width="280dp" @@ -51,9 +51,9 @@ android:textStyle="normal" android:inputType="textPassword" android:textSize="16sp" - android:textColor="?attr/wallpaperTextColor" android:textAppearance="?android:attr/textAppearanceMedium" android:imeOptions="flagForceAscii|actionDone" + android:textCursorDrawable="@null" android:maxLength="500" /> @@ -65,7 +65,7 @@ android:contentDescription="@string/accessibility_ime_switch_button" android:clickable="true" android:padding="8dip" - android:tint="@color/background_protected" + android:tint="?android:attr/textColorPrimary" android:layout_gravity="end|center_vertical" android:background="?android:attr/selectableItemBackground" android:visibility="gone" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index a75b35d117b6..87c98d2e9597 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -46,11 +46,10 @@ android:id="@+id/pinEntry" android:layout_width="@dimen/keyguard_security_width" android:layout_height="match_parent" - android:gravity="center" + style="@style/Widget.TextView.Password" android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_pin_area" /> <View diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index cd61a3775bf7..912d7bbf7ef5 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -64,7 +64,6 @@ android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_sim_pin_area" /> <View diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index bb757356f2b9..81b49648ab62 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -65,7 +65,6 @@ android:layout_centerHorizontal="true" android:layout_marginRight="72dp" androidprv:scaledTextSize="@integer/scaled_password_text_size" - android:textColor="?attr/wallpaperTextColor" android:contentDescription="@string/keyguard_accessibility_sim_puk_area" /> <View diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 401f3e3e0685..bc48e8fe3eea 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -20,41 +20,46 @@ <resources> <!-- Keyguard PIN pad styles --> <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView"> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> <item name="android:textSize">@dimen/kg_status_line_font_size</item> </style> <style name="Keyguard.TextView.EmergencyButton" parent="Theme.SystemUI"> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:textSize">14dp</item> <item name="android:background">@drawable/kg_emergency_button_background</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:paddingLeft">12dp</item> <item name="android:paddingRight">12dp</item> </style> - <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.TextView"> + <style name="Widget.TextView.NumPadKey" parent="@android:style/Widget.DeviceDefault.TextView"> <item name="android:singleLine">true</item> <item name="android:gravity">center_horizontal|center_vertical</item> <item name="android:background">@null</item> <item name="android:textSize">32sp</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> - <item name="android:textColor">?attr/wallpaperTextColor</item> <item name="android:paddingBottom">-16dp</item> + <item name="android:colorControlHighlight">?android:attr/textColorPrimary</item> + </style> + <style name="Widget.TextView.Password" parent="@android:style/Widget.TextView"> + <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + <item name="android:gravity">center</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> </style> - <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.ImageButton"> + <style name="Keyguard.ImageButton.NumPadDelete" parent="@android:style/Widget.DeviceDefault.ImageButton"> <item name="android:src">@drawable/ic_backspace_black_24dp</item> <item name="android:paddingBottom">11sp</item> - <item name="android:tint">@color/pin_delete_color</item> + <item name="android:tint">?android:attr/textColorSecondary</item> <item name="android:tintMode">src_in</item> <item name="android:src">@drawable/ic_backspace_black_24dp</item> </style> - <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.ImageButton"> + <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.DeviceDefault.ImageButton"> <item name="android:src">@drawable/ic_keyboard_tab_36dp</item> <item name="android:paddingBottom">11sp</item> </style> - <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey"> + <style name="Widget.TextView.NumPadKey.Klondike"> <item name="android:textSize">12sp</item> <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> - <item name="android:textColor">?attr/wallpaperTextColorSecondary</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> <item name="android:paddingBottom">0dp</item> </style> @@ -95,15 +100,9 @@ </style> <style name="PasswordTheme" parent="Theme.SystemUI"> - <item name="android:textColor">?attr/wallpaperTextColor</item> - <item name="android:colorControlNormal">?attr/wallpaperTextColor</item> - <item name="android:colorControlActivated">?attr/wallpaperTextColor</item> - </style> - - <style name="PasswordTheme.Light" parent="Theme.SystemUI.Light"> - <item name="android:textColor">?attr/wallpaperTextColor</item> - <item name="android:colorControlNormal">?attr/wallpaperTextColor</item> - <item name="android:colorControlActivated">?attr/wallpaperTextColor</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:colorControlNormal">?android:attr/textColorPrimary</item> + <item name="android:colorControlActivated">?android:attr/textColorPrimary</item> </style> <style name="Theme.SystemUI.KeyguardPresentation"> diff --git a/packages/SystemUI/res/color/pin_delete_color.xml b/packages/SystemUI/res/color/pin_delete_color.xml index 7d4f1321d52f..c1b4cf87e923 100644 --- a/packages/SystemUI/res/color/pin_delete_color.xml +++ b/packages/SystemUI/res/color/pin_delete_color.xml @@ -15,5 +15,5 @@ ~ limitations under the License --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.61" android:color="?attr/wallpaperTextColor" /> + <item android:alpha="0.61" android:color="?android:attr/textColor" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/pin_divider_color.xml b/packages/SystemUI/res/color/pin_divider_color.xml index aff23171eee3..e05772fab8b2 100644 --- a/packages/SystemUI/res/color/pin_divider_color.xml +++ b/packages/SystemUI/res/color/pin_divider_color.xml @@ -15,5 +15,5 @@ ~ limitations under the License --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:alpha="0.45" android:color="?attr/wallpaperTextColorSecondary" /> + <item android:alpha="0.45" android:color="?android:attr/textColorSecondary" /> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml index 24afebde046b..c47959a04fff 100644 --- a/packages/SystemUI/res/color/qs_background_dark.xml +++ b/packages/SystemUI/res/color/qs_background_dark.xml @@ -16,5 +16,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:alpha="1" - android:color="?android:attr/colorBackgroundFloating"/> + android:color="?android:attr/colorBackground"/> </selector> diff --git a/packages/SystemUI/res/drawable/notification_guts_bg.xml b/packages/SystemUI/res/drawable/notification_guts_bg.xml index 2fe6c7b2d1a2..d62687883c35 100644 --- a/packages/SystemUI/res/drawable/notification_guts_bg.xml +++ b/packages/SystemUI/res/drawable/notification_guts_bg.xml @@ -16,7 +16,7 @@ --> <shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/notification_material_background_color" /> + <solid android:color="?android:attr/colorBackground" /> <!--The radius is 1dp smaller than the notification one, to avoid aliasing bugs on the corners --> <corners android:radius="1dp" /> </shape> diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index ae456631c4f1..1e9be2fb9b05 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -19,7 +19,7 @@ android:color="@color/notification_ripple_untinted_color"> <item> <shape> - <solid android:color="@color/notification_material_background_color" /> + <solid android:color="?android:attr/colorBackground" /> </shape> </item> </ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml index b6a8b70bb3e0..1127d3c247fd 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg_dim.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg_dim.xml @@ -17,7 +17,7 @@ <ripple xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape> - <solid android:color="@color/notification_material_background_dimmed_color" /> + <solid android:color="@color/notification_background_dimmed_color" /> </shape> </item> </ripple> diff --git a/packages/SystemUI/res/layout/app_ops_info.xml b/packages/SystemUI/res/layout/app_ops_info.xml index 667c857b6967..ecf572ba4a5c 100644 --- a/packages/SystemUI/res/layout/app_ops_info.xml +++ b/packages/SystemUI/res/layout/app_ops_info.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/layout/disabled_udfps_view.xml new file mode 100644 index 000000000000..aab86613ef45 --- /dev/null +++ b/packages/SystemUI/res/layout/disabled_udfps_view.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.keyguard.DisabledUdfpsView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/disabled_udfps_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@drawable/circle_white" +/> diff --git a/packages/SystemUI/res/layout/feedback_info.xml b/packages/SystemUI/res/layout/feedback_info.xml index 5e847a28558e..7047c1b21961 100644 --- a/packages/SystemUI/res/layout/feedback_info.xml +++ b/packages/SystemUI/res/layout/feedback_info.xml @@ -26,7 +26,7 @@ android:orientation="vertical" android:paddingStart="@*android:dimen/notification_content_margin_start" android:paddingEnd="@*android:dimen/notification_content_margin_end" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@*android:style/Theme.DeviceDefault.Light"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml index 7d45de3fa50d..30ffc32ce1f8 100644 --- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml +++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml @@ -1,67 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/global_actions_container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > - <com.android.systemui.globalactions.GlobalActionsFlatLayout - android:id="@id/global_actions_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:theme="@style/qs_theme" - android:clipChildren="false" - android:clipToPadding="false" - android:layout_marginStart="@dimen/global_actions_side_margin" - > - <LinearLayout - android:id="@android:id/list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="@dimen/global_actions_grid_vertical_padding" - android:paddingBottom="@dimen/global_actions_grid_vertical_padding" - android:orientation="horizontal" - android:gravity="left | center_vertical" - android:translationZ="@dimen/global_actions_translate" - > - <RelativeLayout - android:id="@+id/global_actions_overflow_button" - android:contentDescription="@string/accessibility_menu" - android:layout_width="48dp" - android:layout_height="48dp" - > - <ImageView - android:src="@drawable/ic_more_vert" - android:layout_centerInParent="true" - android:layout_width="24dp" - android:layout_height="24dp" - android:tint="@color/control_more_vert" - /> - </RelativeLayout> - </LinearLayout> - </com.android.systemui.globalactions.GlobalActionsFlatLayout> - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/global_actions_lock_message_container" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:visibility="gone"> - <TextView - android:id="@+id/global_actions_lock_message" - style="@style/TextAppearance.Control.Title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginHorizontal="@dimen/global_actions_side_margin" - android:drawablePadding="12dp" - android:gravity="center" - android:text="@string/global_action_lock_message" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.35"/> - </androidx.constraintlayout.widget.ConstraintLayout> + <include layout="@layout/global_actions_view" /> + + <include layout="@layout/global_actions_lock_view" /> <com.android.systemui.globalactions.MinHeightScrollView android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/global_actions_lock_view.xml b/packages/SystemUI/res/layout/global_actions_lock_view.xml new file mode 100644 index 000000000000..eccc63688065 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_lock_view.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/global_actions_lock_message_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone"> + <TextView + android:id="@+id/global_actions_lock_message" + style="@style/TextAppearance.Control.Title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/global_actions_side_margin" + android:drawablePadding="12dp" + android:gravity="center" + android:text="@string/global_action_lock_message" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.35"/> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_actions_view.xml b/packages/SystemUI/res/layout/global_actions_view.xml new file mode 100644 index 000000000000..454707bc44e2 --- /dev/null +++ b/packages/SystemUI/res/layout/global_actions_view.xml @@ -0,0 +1,52 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<com.android.systemui.globalactions.GlobalActionsFlatLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@id/global_actions_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:theme="@style/qs_theme" + android:clipChildren="false" + android:clipToPadding="false" + android:layout_marginStart="@dimen/global_actions_side_margin" + > + <LinearLayout + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingTop="@dimen/global_actions_grid_vertical_padding" + android:paddingBottom="@dimen/global_actions_grid_vertical_padding" + android:orientation="horizontal" + android:gravity="left | center_vertical" + android:translationZ="@dimen/global_actions_translate" + > + <RelativeLayout + android:id="@+id/global_actions_overflow_button" + android:contentDescription="@string/accessibility_menu" + android:layout_width="48dp" + android:layout_height="48dp" + > + <ImageView + android:src="@drawable/ic_more_vert" + android:layout_centerInParent="true" + android:layout_width="24dp" + android:layout_height="24dp" + android:tint="@color/control_more_vert" + /> + </RelativeLayout> + </LinearLayout> +</com.android.systemui.globalactions.GlobalActionsFlatLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index 6c20c1e95c6d..2bf41d2472be 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -40,18 +40,4 @@ android:visibility="gone" android:pointerIcon="crosshair"/> <include layout="@layout/global_screenshot_static"/> - <FrameLayout - android:id="@+id/global_screenshot_dismiss_button" - android:layout_width="@dimen/screenshot_dismiss_button_tappable_size" - android:layout_height="@dimen/screenshot_dismiss_button_tappable_size" - android:elevation="7dp" - android:visibility="gone" - android:contentDescription="@string/screenshot_dismiss_description"> - <ImageView - android:id="@+id/global_screenshot_dismiss_image" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="@dimen/screenshot_dismiss_button_margin" - android:src="@drawable/screenshot_cancel"/> - </FrameLayout> </com.android.systemui.screenshot.ScreenshotView> diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml index 096ec7ddd004..9f63c4334a8c 100644 --- a/packages/SystemUI/res/layout/global_screenshot_static.xml +++ b/packages/SystemUI/res/layout/global_screenshot_static.xml @@ -17,7 +17,6 @@ <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" - android:fitsSystemWindows="true" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView @@ -62,4 +61,22 @@ </LinearLayout> </HorizontalScrollView> <include layout="@layout/global_screenshot_preview"/> + <FrameLayout + android:id="@+id/global_screenshot_dismiss_button" + android:layout_width="@dimen/screenshot_dismiss_button_tappable_size" + android:layout_height="@dimen/screenshot_dismiss_button_tappable_size" + android:elevation="7dp" + android:visibility="gone" + app:layout_constraintStart_toEndOf="@id/global_screenshot_preview" + app:layout_constraintEnd_toEndOf="@id/global_screenshot_preview" + app:layout_constraintTop_toTopOf="@id/global_screenshot_preview" + app:layout_constraintBottom_toTopOf="@id/global_screenshot_preview" + android:contentDescription="@string/screenshot_dismiss_description"> + <ImageView + android:id="@+id/global_screenshot_dismiss_image" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="@dimen/screenshot_dismiss_button_margin" + android:src="@drawable/screenshot_cancel"/> + </FrameLayout> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml index 10ad8291636e..fcc1aed65470 100644 --- a/packages/SystemUI/res/layout/notification_conversation_info.xml +++ b/packages/SystemUI/res/layout/notification_conversation_info.xml @@ -24,7 +24,7 @@ android:clipChildren="true" android:clipToPadding="true" android:orientation="vertical" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:paddingStart="12dp"> <!-- Package Info --> diff --git a/packages/SystemUI/res/layout/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml index 5399f57c322f..fb75dd348a41 100644 --- a/packages/SystemUI/res/layout/notification_guts.xml +++ b/packages/SystemUI/res/layout/notification_guts.xml @@ -22,5 +22,4 @@ android:focusable="true" android:id="@+id/notification_guts" android:visibility="gone" - android:gravity="top|start" - android:theme="@*android:style/Theme.DeviceDefault.Light"/> + android:gravity="top|start"/> diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml index 253bc328c5b8..dc9d92001351 100644 --- a/packages/SystemUI/res/layout/notification_snooze.xml +++ b/packages/SystemUI/res/layout/notification_snooze.xml @@ -20,7 +20,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:background="@color/notification_material_background_color" + android:background="?android:attr/colorBackground" android:theme="@style/Theme.SystemUI"> <RelativeLayout diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml index e7c7b5fbf890..13572fa9f4f3 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer.xml @@ -26,11 +26,19 @@ android:layout_gravity="center_vertical|center_horizontal" android:background="@android:color/transparent"> + <ImageView + android:id="@+id/primary_footer_icon" + android:layout_width="@dimen/qs_footer_icon_size" + android:layout_height="@dimen/qs_footer_icon_size" + android:gravity="start" + android:layout_marginEnd="8dp" + android:contentDescription="@null" + android:tint="?android:attr/textColorPrimary" /> + <com.android.systemui.util.AutoMarqueeTextView android:id="@+id/footer_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="start" android:layout_weight="1" android:singleLine="true" android:ellipsize="marquee" diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml index 1a356769d334..3e40321aba42 100644 --- a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml +++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml @@ -29,8 +29,8 @@ android:orientation="vertical"> <ImageView android:id="@+id/parental_controls_icon" - android:layout_width="36dip" - android:layout_height="36dip" + android:layout_width="24dip" + android:layout_height="24dip" android:layout_gravity="center_horizontal" /> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 1d4b98242519..75f76b431da8 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -92,13 +92,5 @@ layout="@layout/keyguard_bottom_area" android:visibility="gone" /> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/qs_navbar_scrim" - android:layout_height="96dp" - android:layout_width="match_parent" - android:layout_gravity="bottom" - android:visibility="invisible" - android:background="@drawable/qs_navbar_scrim" /> - <include layout="@layout/status_bar_expanded_plugin_frame"/> </com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml index b5822c889f1c..e33f186dcbb7 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml @@ -58,7 +58,6 @@ android:src="@drawable/status_bar_notification_section_header_clear_btn" android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all" android:scaleType="center" - android:tint="?attr/wallpaperTextColor" android:tintMode="src_in" android:visibility="gone" android:forceHasOverlappingRendering="false" diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml new file mode 100644 index 000000000000..711cd4e4258a --- /dev/null +++ b/packages/SystemUI/res/layout/tv_notification_item.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/tv_notification_panel_width" + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground" + android:orientation="vertical" + android:padding="12dp"> + + <TextView + android:id="@+id/tv_notification_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingBottom="12dp" + android:textColor="@color/tv_notification_text_color" + android:textSize="18sp" /> + + <TextView + android:id="@+id/tv_notification_details" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/tv_notification_text_color" /> + +</LinearLayout> diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml new file mode 100644 index 000000000000..8f00a727b912 --- /dev/null +++ b/packages/SystemUI/res/layout/tv_notification_panel.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tv_notification_panel" + android:layout_width="@dimen/tv_notification_panel_width" + android:layout_height="match_parent" + android:layout_gravity="end" + android:background="@color/tv_notification_background_color" + android:orientation="vertical"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="12dp" + android:paddingTop="24dp" + android:text="@string/tv_notification_panel_title" + android:textColor="@color/tv_notification_text_color" + android:textSize="24sp" + android:textStyle="bold" /> + + <TextView + android:id="@+id/no_tv_notifications" + style="?android:attr/titleTextStyle" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="top|center" + android:paddingTop="24dp" + android:text="@string/tv_notification_panel_no_notifications" + android:textColor="@color/tv_notification_text_color" + android:visibility="gone" /> + + <androidx.leanback.widget.VerticalGridView + android:id="@+id/notifications_list" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index ccd235d54c0b..c0788051efed 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -1,4 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <com.android.systemui.biometrics.UdfpsView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index cb9e178de243..8cc747bf04d6 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -17,22 +17,13 @@ NOTE: You might also want to edit: core/res/res/values-night/*.xml --> <resources> - <!-- The color of the material notification background --> - <color name="notification_material_background_color">@*android:color/notification_material_background_color</color> - <!-- The color of the legacy notifications with customs backgrounds (gingerbread and lollipop.) It's fine to override this color since at that point the shade was dark. --> - <color name="notification_legacy_background_color">@*android:color/notification_material_background_color</color> - - <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#aa000000</color> + <color name="notification_legacy_background_color">@color/GM2_grey_900</color> <!-- The color of the dividing line between grouped notifications while . --> <color name="notification_divider_color">#212121</color> - <!-- The background color of the notification shade --> - <color name="notification_shade_background_color">@color/GM2_grey_900</color> - <!-- The color of the gear shown behind a notification --> <color name="notification_gear_color">@color/GM2_grey_500</color> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 09ec439b183e..0c8c5c443c4a 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -29,7 +29,8 @@ <item>com.android.systemui.util.NotificationChannels</item> <item>com.android.systemui.volume.VolumeUI</item> <item>com.android.systemui.statusbar.tv.TvStatusBar</item> - <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item> + <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item> + <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item> <item>com.android.systemui.statusbar.tv.VpnStatusObserver</item> <item>com.android.systemui.usb.StorageNotification</item> <item>com.android.systemui.power.PowerUI</item> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index be36316d013c..c51e0bf2c31b 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -80,21 +80,12 @@ <!-- The color of the legacy notification background --> <color name="notification_legacy_background_color">#ff1a1a1a</color> - <!-- The color of the material notification background --> - <color name="notification_material_background_color">@*android:color/notification_material_background_color</color> - - <!-- The color of the material notification background when dimmed --> - <color name="notification_material_background_dimmed_color">#ccffffff</color> - <!-- The color of the material notification background when dark --> <color name="notification_material_background_dark_color">#ff333333</color> <!-- The color of the dividing line between grouped notifications. --> <color name="notification_divider_color">#FF616161</color> - <!-- The background color of the notification shade --> - <color name="notification_shade_background_color">@color/GM2_grey_200</color> - <!-- The color of the ripples on the untinted notifications --> <color name="notification_ripple_untinted_color">#28000000</color> diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml index 9b0ae1d61de6..0961f503e7d4 100644 --- a/packages/SystemUI/res/values/colors_tv.xml +++ b/packages/SystemUI/res/values/colors_tv.xml @@ -33,4 +33,7 @@ <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color> <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color> <color name="tv_volume_dialog_accent">#FFDADCE0</color> + + <color name="tv_notification_background_color">#383838</color> + <color name="tv_notification_text_color">#FFFFFF</color> </resources> diff --git a/packages/SystemUI/res/drawable/qs_navbar_scrim.xml b/packages/SystemUI/res/values/dimens_tv.xml index bbb2617db4a0..9545bfd088a0 100644 --- a/packages/SystemUI/res/drawable/qs_navbar_scrim.xml +++ b/packages/SystemUI/res/values/dimens_tv.xml @@ -1,7 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> - <!-- - ~ Copyright (C) 2014 The Android Open Source Project + ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -13,13 +12,8 @@ ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and - ~ limitations under the License + ~ limitations under the License. --> - -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <gradient - android:type="linear" - android:angle="90" - android:startColor="#55000000" - android:endColor="#00000000" /> -</shape>
\ No newline at end of file +<resources> + <dimen name="tv_notification_panel_width">360dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml index 13271d6f7f2e..b51cb5619f0c 100644 --- a/packages/SystemUI/res/values/strings_tv.xml +++ b/packages/SystemUI/res/values/strings_tv.xml @@ -26,4 +26,6 @@ <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] --> <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string> + <string name="tv_notification_panel_title">Notifications</string> + <string name="tv_notification_panel_no_notifications">No Notifications</string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f38e653190b0..0697c5c0084c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -334,7 +334,6 @@ <item name="wallpaperTextColorSecondary">@*android:color/secondary_text_material_light</item> <item name="android:colorError">@*android:color/error_color_material_light</item> <item name="android:colorControlHighlight">#40000000</item> - <item name="passwordStyle">@style/PasswordTheme.Light</item> <item name="shadowRadius">0</item> <!-- Needed for MediaRoute chooser dialog --> @@ -356,8 +355,8 @@ </style> <style name="LockPatternStyle"> - <item name="*android:regularColor">?attr/wallpaperTextColor</item> - <item name="*android:successColor">?attr/wallpaperTextColor</item> + <item name="*android:regularColor">?android:attr/textColorPrimary</item> + <item name="*android:successColor">?android:attr/textColorPrimary</item> <item name="*android:errorColor">?android:attr/colorError</item> </style> @@ -555,8 +554,8 @@ <style name="TextAppearance.NotificationSectionHeaderButton" - parent="@android:style/Widget.Material.Button.Borderless"> - <item name="android:textColor">?attr/wallpaperTextColor</item> + parent="@android:style/Widget.DeviceDefault.Button.Borderless"> + <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> <item name="android:minWidth">0dp</item> diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml index 0c4fd23ca107..cb433f3a6009 100644 --- a/packages/SystemUI/res/values/styles_tv.xml +++ b/packages/SystemUI/res/values/styles_tv.xml @@ -23,4 +23,12 @@ <item name="android:backgroundDimEnabled">false</item> <item name="android:windowDisablePreview">true</item> </style> + + <style name="TvSidePanelTheme"> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:backgroundDimEnabled">false</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowContentOverlay">@null</item> + </style> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index dd57af33b4dc..229d20b6543e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; +import static android.app.ActivityTaskManager.getService; import android.annotation.NonNull; import android.app.Activity; @@ -53,7 +54,6 @@ import com.android.internal.app.IVoiceInteractionManagerService; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; -import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; @@ -70,6 +70,7 @@ public class ActivityManagerWrapper { // Should match the value in AssistManager private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms"; + private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance(); private ActivityManagerWrapper() { } public static ActivityManagerWrapper getInstance() { @@ -102,29 +103,19 @@ public class ActivityManagerWrapper { */ public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) { // Note: The set of running tasks from the system is ordered by recency - try { - List<ActivityManager.RunningTaskInfo> tasks = - ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents); - if (tasks.isEmpty()) { - return null; - } - return tasks.get(0); - } catch (RemoteException e) { + List<ActivityManager.RunningTaskInfo> tasks = + mAtm.getTasks(1, filterOnlyVisibleRecents); + if (tasks.isEmpty()) { return null; } + return tasks.get(0); } /** * @return a list of the recents tasks. */ public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { - try { - return ActivityTaskManager.getService().getRecentTasks(numTasks, - RECENT_IGNORE_UNAVAILABLE, userId).getList(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get recent tasks", e); - return new ArrayList<>(); - } + return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); } /** @@ -133,7 +124,7 @@ public class ActivityManagerWrapper { public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) { ActivityManager.TaskSnapshot snapshot = null; try { - snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution); + snapshot = getService().getTaskSnapshot(taskId, isLowResolution); } catch (RemoteException e) { Log.w(TAG, "Failed to retrieve task snapshot", e); } @@ -149,8 +140,7 @@ public class ActivityManagerWrapper { */ public void invalidateHomeTaskSnapshot(final Activity homeActivity) { try { - ActivityTaskManager.getService().invalidateHomeTaskSnapshot( - homeActivity.getActivityToken()); + getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken()); } catch (RemoteException e) { Log.w(TAG, "Failed to invalidate home snapshot", e); } @@ -208,7 +198,7 @@ public class ActivityManagerWrapper { } }; } - ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner); + getService().startRecentsActivity(intent, eventTime, runner); return true; } catch (Exception e) { return false; @@ -220,7 +210,7 @@ public class ActivityManagerWrapper { */ public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { try { - ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); + getService().cancelRecentsAnimation(restoreHomeRootTaskPosition); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel recents animation", e); } @@ -259,7 +249,7 @@ public class ActivityManagerWrapper { public boolean startActivityFromRecents(int taskId, ActivityOptions options) { try { Bundle optsBundle = options == null ? null : options.toBundle(); - ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle); + getService().startActivityFromRecents(taskId, optsBundle); return true; } catch (Exception e) { return false; @@ -296,7 +286,7 @@ public class ActivityManagerWrapper { */ public void removeTask(final int taskId) { try { - ActivityTaskManager.getService().removeTask(taskId); + getService().removeTask(taskId); } catch (RemoteException e) { Log.w(TAG, "Failed to remove task=" + taskId, e); } @@ -307,7 +297,7 @@ public class ActivityManagerWrapper { */ public void removeAllRecentTasks() { try { - ActivityTaskManager.getService().removeAllVisibleRecentTasks(); + getService().removeAllVisibleRecentTasks(); } catch (RemoteException e) { Log.w(TAG, "Failed to remove all tasks", e); } @@ -318,7 +308,7 @@ public class ActivityManagerWrapper { */ public boolean isScreenPinningActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; + return getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED; } catch (RemoteException e) { return false; } @@ -337,7 +327,7 @@ public class ActivityManagerWrapper { */ public boolean isLockToAppActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; + return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE; } catch (RemoteException e) { return false; } @@ -348,7 +338,7 @@ public class ActivityManagerWrapper { */ public boolean isLockTaskKioskModeActive() { try { - return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; + return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED; } catch (RemoteException e) { return false; } diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java new file mode 100644 index 000000000000..f01b67b7b5c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; + +import android.hardware.biometrics.BiometricSourceType; +import android.view.View; + +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.util.ViewController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Controls when to show the DisabledUdfpsView to unlock the device on the lockscreen. + * If the device is not authenticated, the bouncer will show. + * + * This tap target will only show when: + * - User has UDFPS enrolled + * - UDFPS is currently unavailable see {@link KeyguardUpdateMonitor#shouldListenForUdfps} + */ +@SysUISingleton +public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> implements Dumpable { + @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @NonNull private final KeyguardViewController mKeyguardViewController; + @NonNull private final StatusBarStateController mStatusBarStateController; + + private boolean mIsDozing; + private boolean mIsBouncerShowing; + private boolean mIsKeyguardShowing; + private boolean mRunningFPS; + private boolean mAuthenticated; + + private boolean mShowButton; + + public DisabledUdfpsController( + @NonNull DisabledUdfpsView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, + @NonNull AuthController authController, + @NonNull KeyguardViewController keyguardViewController + ) { + super(view); + mView.setOnClickListener(mOnClickListener); + mView.setSensorProperties(authController.getUdfpsProps().get(0)); + + mStatusBarStateController = statusBarStateController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardViewController = keyguardViewController; + } + + @Override + protected void onViewAttached() { + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); + mIsBouncerShowing = mKeyguardViewController.isBouncerShowing(); + + mStatusBarStateController.addCallback(mStatusBarStateListener); + mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD; + mIsDozing = mStatusBarStateController.isDozing(); + mAuthenticated = false; + } + + @Override + protected void onViewDetached() { + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + } + + private void updateButtonVisibility() { + mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing + && !mIsBouncerShowing && !mRunningFPS; + if (mShowButton) { + mView.setVisibility(View.VISIBLE); + } else { + mView.setVisibility(View.INVISIBLE); + } + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + pw.println("DisabledUdfpsController state:"); + pw.println(" mShowBouncerButton: " + mShowButton); + pw.println(" mIsDozing: " + mIsDozing); + pw.println(" mIsKeyguardShowing: " + mIsKeyguardShowing); + pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); + pw.println(" mRunningFPS: " + mRunningFPS); + pw.println(" mAuthenticated: " + mAuthenticated); + } + + private final View.OnClickListener mOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mKeyguardViewController.showBouncer(/* scrim */ true); + } + }; + + private StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + mIsKeyguardShowing = newState == StatusBarState.KEYGUARD; + updateButtonVisibility(); + } + + @Override + public void onDozingChanged(boolean isDozing) { + mIsDozing = isDozing; + updateButtonVisibility(); + } + }; + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mIsBouncerShowing = bouncer; + updateButtonVisibility(); + } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + mRunningFPS = running && biometricSourceType == FINGERPRINT; + updateButtonVisibility(); + } + + @Override + public void onUserUnlocked() { + mAuthenticated = true; + updateButtonVisibility(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java new file mode 100644 index 000000000000..d8ab780eb6bd --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import android.annotation.NonNull; +import android.content.Context; +import android.graphics.RectF; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.util.AttributeSet; +import android.view.Surface; +import android.widget.Button; +import android.widget.FrameLayout; + +/** + * A full screen view with an oval target where the UDFPS sensor is. + * Controlled by {@link DisabledUdfpsController}. + */ +public class DisabledUdfpsView extends Button { + @NonNull private final RectF mSensorRect; + @NonNull private final Context mContext; + + // Used to obtain the sensor location. + @NonNull private FingerprintSensorPropertiesInternal mSensorProps; + + public DisabledUdfpsView(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + mSensorRect = new RectF(); + } + + public void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) { + mSensorProps = properties; + } + + // The "h" and "w" are the display's height and width relative to its current rotation. + private void updateSensorRect(int h, int w) { + // mSensorProps coordinates assume portrait mode. + mSensorRect.set(mSensorProps.sensorLocationX - mSensorProps.sensorRadius, + mSensorProps.sensorLocationY - mSensorProps.sensorRadius, + mSensorProps.sensorLocationX + mSensorProps.sensorRadius, + mSensorProps.sensorLocationY + mSensorProps.sensorRadius); + + // Transform mSensorRect if the device is in landscape mode. + switch (mContext.getDisplay().getRotation()) { + case Surface.ROTATION_90: + mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom, + h - mSensorRect.left); + break; + case Surface.ROTATION_270: + mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top, + mSensorRect.right); + break; + default: + // Do nothing to stay in portrait mode. + } + + setX(mSensorRect.left); + setY(mSensorRect.top); + setLayoutParams(new FrameLayout.LayoutParams( + (int) (mSensorRect.right - mSensorRect.left), + (int) (mSensorRect.bottom - mSensorRect.top))); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // Always re-compute the layout regardless of whether "changed" is true. It is usually false + // when the device goes from landscape to seascape and vice versa, but mSensorRect and + // its dependencies need to be recalculated to stay at the same physical location on the + // screen. + final int w = getLayoutParams().width; + final int h = getLayoutParams().height; + updateSensorRect(h, w); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java index 487e0d8ea38e..b7d7498e8960 100644 --- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java +++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java @@ -41,6 +41,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.widget.LockPatternUtils; +import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.util.EmergencyDialerConstants; @@ -148,6 +149,17 @@ public class EmergencyButton extends Button { return super.onTouchEvent(event); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int color = Utils.getColorAttrDefaultColor(getContext(), + android.R.attr.textColorSecondary); + setTextColor(color); + setBackground(getContext() + .getDrawable(com.android.systemui.R.drawable.kg_emergency_button_background)); + } + @Override public boolean performLongClick() { return super.performLongClick(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index 217cf701b265..5760565aaab1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -20,6 +20,7 @@ import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL; import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED; import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT; +import android.annotation.CallSuper; import android.content.res.ColorStateList; import android.os.AsyncTask; import android.os.CountDownTimer; @@ -87,6 +88,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey @Override protected void onViewAttached() { + super.onViewAttached(); mView.setKeyDownListener(mKeyDownListener); mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled()); EmergencyButton button = mView.findViewById(R.id.emergency_call_button); @@ -110,6 +112,13 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey } } + @CallSuper + @Override + public void reloadColors() { + super.reloadColors(); + mMessageAreaController.reloadColors(); + } + @Override public boolean needsInput() { return false; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index c5c36e920d45..829ff9771fb4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -62,6 +62,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private AnimatableClockController mNewLockScreenClockViewController; private FrameLayout mNewLockScreenClockFrame; private AnimatableClockController mNewLockScreenLargeClockViewController; + private FrameLayout mNewLockScreenLargeClockFrame; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @@ -126,6 +127,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mView.updateColors(getGradientColors()); updateAodIcons(); mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view); + mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large); } @Override @@ -199,13 +201,18 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS /** * Update position of the view, with optional animation. Move the slice view and the clock * slightly towards the center in order to prevent burn-in. Y positioning occurs at the - * view parent level. + * view parent level. The large clock view will scale instead of using x position offsets, to + * keep the clock centered. */ - void updatePosition(int x, AnimationProperties props, boolean animate) { + void updatePosition(int x, float scale, AnimationProperties props, boolean animate) { x = Math.abs(x); if (mNewLockScreenClockFrame != null) { PropertyAnimator.setProperty(mNewLockScreenClockFrame, AnimatableProperty.TRANSLATION_X, -x, props, animate); + PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_X, + scale, props, animate); + PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y, + scale, props, animate); } mKeyguardSliceViewController.updatePosition(x, props, animate); mNotificationIconAreaController.updatePosition(x, props, animate); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index fbda818740e8..6aa5e0df3653 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.annotation.CallSuper; import android.content.res.ColorStateList; import android.content.res.Resources; import android.telephony.TelephonyManager; @@ -24,6 +25,7 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -37,6 +39,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> private final SecurityMode mSecurityMode; private final KeyguardSecurityCallback mKeyguardSecurityCallback; + private final EmergencyButton mEmergencyButton; private boolean mPaused; @@ -68,6 +71,7 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> super(view); mSecurityMode = securityMode; mKeyguardSecurityCallback = keyguardSecurityCallback; + mEmergencyButton = view == null ? null : view.findViewById(R.id.emergency_call_button); } @Override @@ -112,6 +116,16 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> public void showMessage(CharSequence message, ColorStateList colorState) { } + /** + * Reload colors from resources. + **/ + @CallSuper + public void reloadColors() { + if (mEmergencyButton != null) { + mEmergencyButton.reloadColors(); + } + } + public void startAppearAnimation() { mView.startAppearAnimation(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index 1a0a4370fca4..561ea4075291 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -29,6 +29,7 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.lang.ref.WeakReference; @@ -69,7 +70,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp void onThemeChanged() { TypedArray array = mContext.obtainStyledAttributes(new int[] { - R.attr.wallpaperTextColor + android.R.attr.textColor }); ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED)); array.recycle(); @@ -77,6 +78,11 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } + void reloadColor() { + mDefaultColorState = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); + update(); + } + void onDensityOrFontScaleChanged() { TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] { android.R.attr.textSize diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index 1618e8e58055..6e40f025da50 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -93,6 +93,13 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag mView.setNextMessageColor(colorState); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + mView.reloadColor(); + } + /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */ public static class Factory { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index d34ea8c5e018..5e339172ca28 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.content.res.ColorStateList; import android.content.res.Resources; import android.os.UserHandle; import android.text.Editable; @@ -30,12 +31,14 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; -import android.widget.TextView; +import android.widget.EditText; +import android.widget.ImageView; import android.widget.TextView.OnEditorActionListener; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -51,8 +54,8 @@ public class KeyguardPasswordViewController private final InputMethodManager mInputMethodManager; private final DelayableExecutor mMainExecutor; private final boolean mShowImeAtScreenOn; - private TextView mPasswordEntry; - private View mSwitchImeButton; + private EditText mPasswordEntry; + private ImageView mSwitchImeButton; private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> { // Check if this was the result of hitting the enter key @@ -88,6 +91,18 @@ public class KeyguardPasswordViewController } }; + @Override + public void reloadColors() { + super.reloadColors(); + int textColor = Utils.getColorAttr(mView.getContext(), + android.R.attr.textColorPrimary).getDefaultColor(); + mPasswordEntry.setTextColor(textColor); + mPasswordEntry.setHighlightColor(textColor); + mPasswordEntry.setBackgroundTintList(ColorStateList.valueOf(textColor)); + mPasswordEntry.setForegroundTintList(ColorStateList.valueOf(textColor)); + mSwitchImeButton.setImageTintList(ColorStateList.valueOf(textColor)); + } + protected KeyguardPasswordViewController(KeyguardPasswordView view, KeyguardUpdateMonitor keyguardUpdateMonitor, SecurityMode securityMode, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 730c17787908..2aaf748e2415 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -33,6 +33,7 @@ import com.android.internal.widget.LockPatternView.Cell; import com.android.internal.widget.LockscreenCredential; import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.util.List; @@ -197,6 +198,7 @@ public class KeyguardPatternViewController @Override protected void onViewAttached() { + super.onViewAttached(); mLockPatternView.setOnPatternListener(new UnlockPatternListener()); mLockPatternView.setSaveEnabled(false); mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( @@ -252,6 +254,16 @@ public class KeyguardPatternViewController } @Override + public void reloadColors() { + super.reloadColors(); + mMessageAreaController.reloadColors(); + int textColor = Utils.getColorAttr(mLockPatternView.getContext(), + android.R.attr.textColorPrimary).getDefaultColor(); + int errorColor = Utils.getColorError(mLockPatternView.getContext()).getDefaultColor(); + mLockPatternView.setColors(textColor, textColor, errorColor); + } + + @Override public void onPause() { super.onPause(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 7fa43116a7b1..4ddfccb21c73 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -24,12 +24,16 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Rect; import android.util.AttributeSet; +import android.view.ContextThemeWrapper; import android.view.KeyEvent; import android.view.View; +import android.widget.ImageButton; import com.android.internal.widget.LockscreenCredential; +import com.android.settingslib.Utils; import com.android.systemui.R; /** @@ -39,8 +43,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView protected PasswordTextView mPasswordEntry; private View mOkButton; - private View mDeleteButton; - private View[] mButtons = new View[10]; + private ImageButton mDeleteButton; + private NumPadKey[] mButtons = new NumPadKey[10]; + private View mDivider; public KeyguardPinBasedInputView(Context context) { this(context, null); @@ -147,6 +152,7 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView mDeleteButton = findViewById(R.id.delete_button); mDeleteButton.setVisibility(View.VISIBLE); + mDivider = findViewById(R.id.divider); mButtons[0] = findViewById(R.id.key0); mButtons[1] = findViewById(R.id.key1); @@ -161,6 +167,26 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView mPasswordEntry.requestFocus(); super.onFinishInflate(); + reloadColors(); + } + + /** + * Reload colors from resources. + **/ + public void reloadColors() { + for (NumPadKey key : mButtons) { + key.reloadColors(); + } + mPasswordEntry.reloadColors(); + int deleteColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary) + .getDefaultColor(); + mDeleteButton.setImageTintList(ColorStateList.valueOf(deleteColor)); + mDivider.setBackground(getContext().getDrawable(R.drawable.pin_divider)); + + ContextThemeWrapper themedContext = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey); + mDeleteButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin)); + mOkButton.setBackground(themedContext.getDrawable(R.drawable.ripple_drawable_pin)); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 6769436be8ef..fb0d6beca513 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -53,6 +53,12 @@ public class KeyguardPinViewController } @Override + public void reloadColors() { + super.reloadColors(); + mView.reloadColors(); + } + + @Override void resetState() { super.resetState(); mMessageAreaController.setMessage(""); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 9a511502b475..1a8d420fb394 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -47,6 +47,7 @@ import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; @@ -69,6 +70,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; private final SecurityCallback mSecurityCallback; + private final ConfigurationController mConfigurationController; private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; @@ -144,6 +146,18 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } } }; + private ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onOverlayChanged() { + mSecurityViewFlipperController.reloadColors(); + } + + @Override + public void onUiModeChanged() { + mSecurityViewFlipperController.reloadColors(); + } + }; private KeyguardSecurityContainerController(KeyguardSecurityContainer view, AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, @@ -154,7 +168,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, SecurityCallback securityCallback, - KeyguardSecurityViewFlipperController securityViewFlipperController) { + KeyguardSecurityViewFlipperController securityViewFlipperController, + ConfigurationController configurationController) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -166,6 +181,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mSecurityViewFlipperController = securityViewFlipperController; mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( mKeyguardSecurityCallback); + mConfigurationController = configurationController; } @Override @@ -176,10 +192,12 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override protected void onViewAttached() { mView.setSwipeListener(mSwipeListener); + mConfigurationController.addCallback(mConfigurationListener); } @Override protected void onViewDetached() { + mConfigurationController.removeCallback(mConfigurationListener); } /** */ @@ -459,6 +477,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final UiEventLogger mUiEventLogger; private final KeyguardStateController mKeyguardStateController; private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; + private final ConfigurationController mConfigurationController; @Inject Factory(KeyguardSecurityContainer view, @@ -470,7 +489,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard MetricsLogger metricsLogger, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - KeyguardSecurityViewFlipperController securityViewFlipperController) { + KeyguardSecurityViewFlipperController securityViewFlipperController, + ConfigurationController configurationController) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -480,6 +500,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; mSecurityViewFlipperController = securityViewFlipperController; + mConfigurationController = configurationController; } public KeyguardSecurityContainerController create( @@ -487,7 +508,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard return new KeyguardSecurityContainerController(mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, securityCallback, mSecurityViewFlipperController); + mKeyguardStateController, securityCallback, mSecurityViewFlipperController, + mConfigurationController); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java index 49530355a6fb..f1b504e9f941 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -71,6 +71,15 @@ public class KeyguardSecurityViewFlipperController } } + /** + * Reload colors of ui elements upon theme change. + */ + public void reloadColors() { + for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { + child.reloadColors(); + } + } + @VisibleForTesting KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 2cdd7f117594..5b4a7ff3e16e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -309,7 +309,7 @@ public class KeyguardSimPinViewController Resources rez = mView.getResources(); String msg; TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); + new int[] { android.R.attr.textColor }); int color = array.getColor(0, Color.WHITE); array.recycle(); if (count < 2) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index adb4c13b74d5..eafb33f8195d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -189,7 +189,7 @@ public class KeyguardSimPukViewController Resources rez = mView.getResources(); String msg; TypedArray array = mView.getContext().obtainStyledAttributes( - new int[] { R.attr.wallpaperTextColor }); + new int[] { android.R.attr.textColor }); int color = array.getColor(0, Color.WHITE); array.recycle(); if (count < 2) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index a32cd1420fdc..3cbab8e66fdb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -244,7 +244,7 @@ public class KeyguardSliceView extends LinearLayout { iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize); } } - button.setCompoundDrawables(iconDrawable, null, null, null); + button.setCompoundDrawablesRelative(iconDrawable, null, null, null); button.setOnClickListener(mOnClickListener); button.setClickable(pendingIntent != null); } @@ -536,9 +536,9 @@ public class KeyguardSliceView extends LinearLayout { } @Override - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, + public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, Drawable bottom) { - super.setCompoundDrawables(left, top, right, bottom); + super.setCompoundDrawablesRelative(start, top, end, bottom); updateDrawableColors(); updatePadding(); } @@ -558,9 +558,9 @@ public class KeyguardSliceView extends LinearLayout { public void setLockScreenMode(int mode) { mLockScreenMode = mode; if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { - setGravity(Gravity.START); + setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); } else { - setGravity(Gravity.CENTER); + setTextAlignment(View.TEXT_ALIGNMENT_CENTER); } updatePadding(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index c0e06e87b753..bc81a198c7e6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -193,7 +193,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV /** * Update position of the view with an optional animation */ - public void updatePosition(int x, int y, boolean animate) { + public void updatePosition(int x, int y, float scale, boolean animate) { PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES, animate); @@ -202,10 +202,12 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV PropertyAnimator.setProperty(mView, AnimatableProperty.X, 0, CLOCK_ANIMATION_PROPERTIES, animate); - mKeyguardClockSwitchController.updatePosition(x, CLOCK_ANIMATION_PROPERTIES, animate); + mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES, + animate); } else { // reset any prior movement - mKeyguardClockSwitchController.updatePosition(0, CLOCK_ANIMATION_PROPERTIES, animate); + mKeyguardClockSwitchController.updatePosition(0, 0f, CLOCK_ANIMATION_PROPERTIES, + animate); PropertyAnimator.setProperty(mView, AnimatableProperty.X, x, CLOCK_ANIMATION_PROPERTIES, animate); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 611131f6e216..a7e51951e9cd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1925,6 +1925,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } // TODO: Add support for multiple fingerprint sensors, b/173730729 + updateUdfpsEnrolled(getCurrentUser()); boolean shouldListenForFingerprint = isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint(); boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING @@ -2137,7 +2138,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (DEBUG) Log.v(TAG, "startListeningForFingerprint()"); int userId = getCurrentUser(); - updateUdfpsEnrolled(userId); if (isUnlockWithFingerprintPossible(userId)) { if (mFingerprintCancelSignal != null) { mFingerprintCancelSignal.cancel(); @@ -2627,7 +2627,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab Assert.isMainThread(); if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncerVisible + ")"); mBouncer = bouncerVisible == 1; - if (mBouncer) { // If the bouncer is shown, always clear this flag. This can happen in the following // situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 2205fdd4267d..a5182055e14d 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -18,9 +18,11 @@ package com.android.keyguard; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.os.PowerManager; import android.os.SystemClock; import android.util.AttributeSet; +import android.view.ContextThemeWrapper; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -30,6 +32,7 @@ import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; +import com.android.settingslib.Utils; import com.android.systemui.R; public class NumPadKey extends ViewGroup { @@ -121,12 +124,29 @@ public class NumPadKey extends ViewGroup { a = context.obtainStyledAttributes(attrs, android.R.styleable.View); if (!a.hasValueOrEmpty(android.R.styleable.View_background)) { - setBackground(mContext.getDrawable(R.drawable.ripple_drawable_pin)); + Drawable rippleDrawable = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin); + setBackground(rippleDrawable); } a.recycle(); setContentDescription(mDigitText.getText().toString()); } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) + .getDefaultColor(); + int klondikeColor = Utils.getColorAttr(getContext(), android.R.attr.textColorSecondary) + .getDefaultColor(); + mDigitText.setTextColor(textColor); + mKlondikeText.setTextColor(klondikeColor); + Drawable rippleDrawable = new ContextThemeWrapper(mContext, + R.style.Widget_TextView_NumPadKey).getDrawable(R.drawable.ripple_drawable_pin); + setBackground(rippleDrawable); + } + @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index c92174a0d8af..5ffc2836b9e3 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -42,6 +42,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.EditText; +import com.android.settingslib.Utils; import com.android.systemui.R; import java.util.ArrayList; @@ -131,8 +132,8 @@ public class PasswordTextView extends View { mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding, getContext().getResources().getDimensionPixelSize( R.dimen.password_char_padding)); - int textColor = a.getColor(R.styleable.PasswordTextView_android_textColor, Color.WHITE); - mDrawPaint.setColor(textColor); + mDrawPaint.setColor(a.getColor(R.styleable.PasswordTextView_android_textColor, + Color.WHITE)); } finally { a.recycle(); } @@ -184,6 +185,15 @@ public class PasswordTextView extends View { } } + /** + * Reload colors from resources. + **/ + public void reloadColors() { + int textColor = Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary) + .getDefaultColor(); + mDrawPaint.setColor(textColor); + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt index f5e01dedfe3e..0d41a2f56618 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt @@ -19,8 +19,9 @@ import android.graphics.Canvas import android.graphics.Paint import android.graphics.fonts.Font import android.graphics.text.PositionedGlyphs -import android.graphics.text.TextRunShaper import android.text.Layout +import android.text.TextPaint +import android.text.TextShaper import android.util.MathUtils import com.android.internal.graphics.ColorUtils import java.lang.Math.max @@ -57,10 +58,10 @@ class TextInterpolator( */ val targetPaint = createDefaultPaint(layout.paint, lines) - private fun createDefaultPaint(paint: Paint, lines: Int): ArrayList<Paint> { - val paintList = ArrayList<Paint>() + private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> { + val paintList = ArrayList<TextPaint>() for (i in 0 until lines) - paintList.add(Paint(paint)) + paintList.add(TextPaint(paint)) return paintList } @@ -79,9 +80,9 @@ class TextInterpolator( } /** - * A class represents text layout of a single line. + * A class represents text layout of a single run. */ - private class Line( + private class Run( val glyphIds: IntArray, val baseX: FloatArray, // same length as glyphIds val baseY: FloatArray, // same length as glyphIds @@ -90,11 +91,18 @@ class TextInterpolator( val fontRuns: List<FontRun> ) + /** + * A class represents text layout of a single line. + */ + private class Line( + val runs: List<Run> + ) + private var lines = listOf<Line>() private val fontInterpolator = FontInterpolator() // Recycling object for glyph drawing. Will be extended for the longest font run if needed. - private val tmpDrawPaints = ArrayList<Paint>() + private val tmpDrawPaints = ArrayList<TextPaint>() private var tmpPositionArray = FloatArray(20) /** @@ -215,12 +223,14 @@ class TextInterpolator( } lines.forEach { line -> - for (i in line.baseX.indices) { - line.baseX[i] = MathUtils.lerp(line.baseX[i], line.targetX[i], progress) - line.baseY[i] = MathUtils.lerp(line.baseY[i], line.targetY[i], progress) - } - line.fontRuns.forEach { - it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress) + line.runs.forEach { run -> + for (i in run.baseX.indices) { + run.baseX[i] = MathUtils.lerp(run.baseX[i], run.targetX[i], progress) + run.baseY[i] = MathUtils.lerp(run.baseY[i], run.targetY[i], progress) + } + run.fontRuns.forEach { + it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress) + } } } @@ -228,10 +238,10 @@ class TextInterpolator( } companion object { - fun updatePaint(toUpdate: ArrayList<Paint>, newValues: ArrayList<Paint>) { + fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) { toUpdate.clear() for (paint in newValues) - toUpdate.add(Paint(paint)) + toUpdate.add(TextPaint(paint)) } } @@ -243,20 +253,22 @@ class TextInterpolator( fun draw(canvas: Canvas) { lerp(basePaint, targetPaint, progress, tmpDrawPaints) lines.forEachIndexed { lineNo, line -> - canvas.save() - try { - // Move to drawing origin. - val origin = layout.getDrawOrigin(lineNo) - canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) - - line.fontRuns.forEach { run -> - if (lineNo >= tmpDrawPaints.size) - drawFontRun(canvas, line, run, tmpDrawPaints[0]) - else - drawFontRun(canvas, line, run, tmpDrawPaints[lineNo]) + line.runs.forEach { run -> + canvas.save() + try { + // Move to drawing origin. + val origin = layout.getDrawOrigin(lineNo) + canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat()) + + run.fontRuns.forEach { fontRun -> + if (lineNo >= tmpDrawPaints.size) + drawFontRun(canvas, run, fontRun, tmpDrawPaints[0]) + else + drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo]) + } + } finally { + canvas.restore() } - } finally { - canvas.restore() } } } @@ -271,64 +283,68 @@ class TextInterpolator( } var maxRunLength = 0 - lines = baseLayout.zip(targetLayout) { base, target -> - require(base.glyphCount() == target.glyphCount()) { - "Inconsistent glyph count at line ${lines.size}" - } - - val glyphCount = base.glyphCount() + lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> + val runs = baseLine.zip(targetLine) { base, target -> - // Good to recycle the array if the existing array can hold the new layout result. - val glyphIds = IntArray(glyphCount) { - base.getGlyphId(it).also { baseGlyphId -> - require(baseGlyphId == target.getGlyphId(it)) { - "Inconsistent glyph ID at $it in line ${lines.size}" - } + require(base.glyphCount() == target.glyphCount()) { + "Inconsistent glyph count at line ${lines.size}" } - } - val baseX = FloatArray(glyphCount) { base.getGlyphX(it) } - val baseY = FloatArray(glyphCount) { base.getGlyphY(it) } - val targetX = FloatArray(glyphCount) { target.getGlyphX(it) } - val targetY = FloatArray(glyphCount) { target.getGlyphY(it) } - - // Calculate font runs - val fontRun = mutableListOf<FontRun>() - if (glyphCount != 0) { - var start = 0 - var baseFont = base.getFont(start) - var targetFont = target.getFont(start) - require(FontInterpolator.canInterpolate(baseFont, targetFont)) { - "Cannot interpolate font at $start ($baseFont vs $targetFont)" + val glyphCount = base.glyphCount() + + // Good to recycle the array if the existing array can hold the new layout result. + val glyphIds = IntArray(glyphCount) { + base.getGlyphId(it).also { baseGlyphId -> + require(baseGlyphId == target.getGlyphId(it)) { + "Inconsistent glyph ID at $it in line ${lines.size}" + } + } } - for (i in 1 until glyphCount) { - val nextBaseFont = base.getFont(i) - val nextTargetFont = target.getFont(i) + val baseX = FloatArray(glyphCount) { base.getGlyphX(it) } + val baseY = FloatArray(glyphCount) { base.getGlyphY(it) } + val targetX = FloatArray(glyphCount) { target.getGlyphX(it) } + val targetY = FloatArray(glyphCount) { target.getGlyphY(it) } + + // Calculate font runs + val fontRun = mutableListOf<FontRun>() + if (glyphCount != 0) { + var start = 0 + var baseFont = base.getFont(start) + var targetFont = target.getFont(start) + require(FontInterpolator.canInterpolate(baseFont, targetFont)) { + "Cannot interpolate font at $start ($baseFont vs $targetFont)" + } - if (baseFont !== nextBaseFont) { - require(targetFont !== nextTargetFont) { - "Base font has changed at $i but target font has not changed." - } - // Font transition point. push run and reset context. - fontRun.add(FontRun(start, i, baseFont, targetFont)) - maxRunLength = max(maxRunLength, i - start) - baseFont = nextBaseFont - targetFont = nextTargetFont - start = i - require(FontInterpolator.canInterpolate(baseFont, targetFont)) { - "Cannot interpolate font at $start ($baseFont vs $targetFont)" - } - } else { // baseFont === nextBaseFont - require(targetFont === nextTargetFont) { - "Base font has not changed at $i but target font has changed." + for (i in 1 until glyphCount) { + val nextBaseFont = base.getFont(i) + val nextTargetFont = target.getFont(i) + + if (baseFont !== nextBaseFont) { + require(targetFont !== nextTargetFont) { + "Base font has changed at $i but target font has not changed." + } + // Font transition point. push run and reset context. + fontRun.add(FontRun(start, i, baseFont, targetFont)) + maxRunLength = max(maxRunLength, i - start) + baseFont = nextBaseFont + targetFont = nextTargetFont + start = i + require(FontInterpolator.canInterpolate(baseFont, targetFont)) { + "Cannot interpolate font at $start ($baseFont vs $targetFont)" + } + } else { // baseFont === nextBaseFont + require(targetFont === nextTargetFont) { + "Base font has not changed at $i but target font has changed." + } } } + fontRun.add(FontRun(start, glyphCount, baseFont, targetFont)) + maxRunLength = max(maxRunLength, glyphCount - start) } - fontRun.add(FontRun(start, glyphCount, baseFont, targetFont)) - maxRunLength = max(maxRunLength, glyphCount - start) + Run(glyphIds, baseX, baseY, targetX, targetY, fontRun) } - Line(glyphIds, baseX, baseY, targetX, targetY, fontRun) + Line(runs) } // Update float array used for drawing. @@ -338,7 +354,7 @@ class TextInterpolator( } // Draws single font run. - private fun drawFontRun(c: Canvas, line: Line, run: FontRun, paint: Paint) { + private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) { var arrayIndex = 0 for (i in run.start until run.end) { tmpPositionArray[arrayIndex++] = @@ -358,7 +374,7 @@ class TextInterpolator( } private fun updatePositionsAndFonts( - layoutResult: List<PositionedGlyphs>, + layoutResult: List<List<PositionedGlyphs>>, updateBase: Boolean ) { // Update target positions with newly calculated text layout. @@ -366,45 +382,48 @@ class TextInterpolator( "The new layout result has different line count." } - lines.zip(layoutResult) { line, newGlyphs -> - require(newGlyphs.glyphCount() == line.glyphIds.size) { - "The new layout has different glyph count." - } + lines.zip(layoutResult) { line, runs -> + line.runs.zip(runs) { lineRun, newGlyphs -> + require(newGlyphs.glyphCount() == lineRun.glyphIds.size) { + "The new layout has different glyph count." + } - line.fontRuns.forEach { run -> - val newFont = newGlyphs.getFont(run.start) - for (i in run.start until run.end) { - require(newGlyphs.getGlyphId(run.start) == line.glyphIds[run.start]) { - "The new layout has different glyph ID at ${run.start}" + lineRun.fontRuns.forEach { run -> + val newFont = newGlyphs.getFont(run.start) + for (i in run.start until run.end) { + require(newGlyphs.getGlyphId(run.start) == lineRun.glyphIds[run.start]) { + "The new layout has different glyph ID at ${run.start}" + } + require(newFont === newGlyphs.getFont(i)) { + "The new layout has different font run." + + " $newFont vs ${newGlyphs.getFont(i)} at $i" + } } - require(newFont === newGlyphs.getFont(i)) { - "The new layout has different font run." + - " $newFont vs ${newGlyphs.getFont(i)} at $i" + + // The passing base font and target font is already interpolatable, so just + // check new font can be interpolatable with base font. + require(FontInterpolator.canInterpolate(newFont, run.baseFont)) { + "New font cannot be interpolated with existing font. $newFont," + + " ${run.baseFont}" } - } - // The passing base font and target font is already interpolatable, so just check - // new font can be interpolatable with base font. - require(FontInterpolator.canInterpolate(newFont, run.baseFont)) { - "New font cannot be interpolated with existing font. $newFont, ${run.baseFont}" + if (updateBase) { + run.baseFont = newFont + } else { + run.targetFont = newFont + } } if (updateBase) { - run.baseFont = newFont + for (i in lineRun.baseX.indices) { + lineRun.baseX[i] = newGlyphs.getGlyphX(i) + lineRun.baseY[i] = newGlyphs.getGlyphY(i) + } } else { - run.targetFont = newFont - } - } - - if (updateBase) { - for (i in line.baseX.indices) { - line.baseX[i] = newGlyphs.getGlyphX(i) - line.baseY[i] = newGlyphs.getGlyphY(i) - } - } else { - for (i in line.baseX.indices) { - line.targetX[i] = newGlyphs.getGlyphX(i) - line.targetY[i] = newGlyphs.getGlyphY(i) + for (i in lineRun.baseX.indices) { + lineRun.targetX[i] = newGlyphs.getGlyphX(i) + lineRun.targetY[i] = newGlyphs.getGlyphY(i) + } } } } @@ -412,16 +431,16 @@ class TextInterpolator( // Linear interpolate the paint. private fun lerp( - from: ArrayList<Paint>, - to: ArrayList<Paint>, + from: ArrayList<TextPaint>, + to: ArrayList<TextPaint>, progress: Float, - out: ArrayList<Paint> + out: ArrayList<TextPaint> ) { out.clear() // Currently only font size & colors are interpolated. // TODO(172943390): Add other interpolation or support custom interpolator. for (index in from.indices) { - val paint = Paint(from[index]) + val paint = TextPaint(from[index]) paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress) paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress) out.add(paint) @@ -429,18 +448,20 @@ class TextInterpolator( } // Shape the text and stores the result to out argument. - private fun shapeText(layout: Layout, paints: ArrayList<Paint>): List<PositionedGlyphs> { - val out = mutableListOf<PositionedGlyphs>() + private fun shapeText( + layout: Layout, + paints: ArrayList<TextPaint> + ): List<List<PositionedGlyphs>> { + val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. val lineStart = layout.getLineStart(lineNo) val count = layout.getLineEnd(lineNo) - lineStart - out.add(TextRunShaper.shapeTextRun( - layout.text, // Styles are ignored. - lineStart, count, // shape range - lineStart, count, // shape context = shape range. - 0f, 0f, // the layout offset. Not changed. - layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT, - paints[lineNo])) // Use given paint instead of layout's for style interpolation. + val runs = mutableListOf<PositionedGlyphs>() + TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, + paints[lineNo]) { _, _, glyphs, _ -> + runs.add(glyphs) + } + out.add(runs) } return out } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 8e5e9ea257ba..6b4e8bdef72a 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -113,8 +113,10 @@ public class SystemUIFactory { .setShellCommandHandler(mWMComponent.getShellCommandHandler()) .setAppPairs(mWMComponent.getAppPairs()); } else { - // TODO: Call on prepareSysUIComponentBuilder but not with real components. - builder = builder.setPip(Optional.ofNullable(null)) + // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option + // is separating this logic into newly creating SystemUITestsFactory. + builder = prepareSysUIComponentBuilder(builder, mWMComponent) + .setPip(Optional.ofNullable(null)) .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) .setBubbles(Optional.ofNullable(null)) @@ -194,4 +196,4 @@ public class SystemUIFactory { AssetManager am, String modelName) { return new BackGestureTfClassifierProvider(); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index dc841d91b1ab..9edfee73936e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -22,6 +22,7 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; @@ -74,7 +75,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, private final CommandQueue mCommandQueue; private final StatusBarStateController mStatusBarStateController; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @Nullable private final FaceManager mFaceManager; private final Provider<UdfpsController> mUdfpsControllerFactory; @@ -313,7 +314,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, @Inject public AuthController(Context context, CommandQueue commandQueue, StatusBarStateController statusBarStateController, - IActivityTaskManager activityTaskManager, + ActivityTaskManager activityTaskManager, @Nullable FingerprintManager fingerprintManager, @Nullable FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { @@ -356,12 +357,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mUdfpsController = mUdfpsControllerFactory.get(); } - try { - mTaskStackListener = new BiometricTaskStackListener(); - mActivityTaskManager.registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Log.w(TAG, "Unable to register task stack listener", e); - } + mTaskStackListener = new BiometricTaskStackListener(); + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } @Override @@ -413,6 +410,11 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mCurrentDialog.onHelp(message); } + @Nullable + public List<FingerprintSensorPropertiesInternal> getUdfpsProps() { + return mUdfpsProps; + } + private String getErrorString(int modality, int error, int vendorCode) { switch (modality) { case TYPE_FACE: diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index f6b8b4c92049..e24a513437ea 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,44 +14,39 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS; +import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS; -import android.app.ActivityManager; -import android.content.res.Resources; import android.net.Uri; import android.os.Build; import android.util.IndentingPrintWriter; import android.util.Log; import android.view.MotionEvent; -import android.view.ViewConfiguration; import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; -import com.android.systemui.R; -import com.android.systemui.classifier.Classifier; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.classifier.FalsingDataProvider.SessionListener; -import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.phone.NotificationTapHelper; -import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayDeque; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Queue; +import java.util.Set; import java.util.StringJoiner; import java.util.stream.Collectors; import javax.inject.Inject; +import javax.inject.Named; /** * FalsingManager designed to make clear why a touch was rejected. @@ -68,6 +63,7 @@ public class BrightLineFalsingManager implements FalsingManager { private final DockManager mDockManager; private final SingleTapClassifier mSingleTapClassifier; private final DoubleTapClassifier mDoubleTapClassifier; + private final boolean mTestHarness; private final MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; private static final Queue<String> RECENT_INFO_LOG = @@ -75,7 +71,7 @@ public class BrightLineFalsingManager implements FalsingManager { private static final Queue<DebugSwipeRecord> RECENT_SWIPES = new ArrayDeque<>(RECENT_SWIPE_LOG_SIZE + 1); - private final List<FalsingClassifier> mClassifiers; + private final Collection<FalsingClassifier> mClassifiers; private final SessionListener mSessionListener = new SessionListener() { @Override @@ -93,29 +89,17 @@ public class BrightLineFalsingManager implements FalsingManager { @Inject public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, - DeviceConfigProxy deviceConfigProxy, @Main Resources resources, - ViewConfiguration viewConfiguration, DockManager dockManager) { + DockManager dockManager, MetricsLogger metricsLogger, + @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers, + SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier, + @TestHarness boolean testHarness) { mDataProvider = falsingDataProvider; mDockManager = dockManager; - - mMetricsLogger = new MetricsLogger(); - mClassifiers = new ArrayList<>(); - DistanceClassifier distanceClassifier = - new DistanceClassifier(mDataProvider, deviceConfigProxy); - ProximityClassifier proximityClassifier = - new ProximityClassifier(distanceClassifier, mDataProvider, deviceConfigProxy); - mClassifiers.add(new PointerCountClassifier(mDataProvider)); - mClassifiers.add(new TypeClassifier(mDataProvider)); - mClassifiers.add(new DiagonalClassifier(mDataProvider, deviceConfigProxy)); - mClassifiers.add(distanceClassifier); - mClassifiers.add(proximityClassifier); - mClassifiers.add(new ZigZagClassifier(mDataProvider, deviceConfigProxy)); - - mSingleTapClassifier = new SingleTapClassifier( - mDataProvider, viewConfiguration.getScaledTouchSlop()); - mDoubleTapClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, - resources.getDimension(R.dimen.double_tap_slop), - NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS); + mMetricsLogger = metricsLogger; + mClassifiers = classifiers; + mSingleTapClassifier = singleTapClassifier; + mDoubleTapClassifier = doubleTapClassifier; + mTestHarness = testHarness; mDataProvider.addSessionListener(mSessionListener); } @@ -132,7 +116,7 @@ public class BrightLineFalsingManager implements FalsingManager { return mPreviousResult; } - mPreviousResult = !ActivityManager.isRunningInUserTestHarness() + mPreviousResult = !mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> { boolean result = falsingClassifier.isFalseTouch(); @@ -185,8 +169,12 @@ public class BrightLineFalsingManager implements FalsingManager { return true; } - // TODO(b/172655679): we always reject single-taps when doing a robust check for now. - return robustCheck; + // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed + if (robustCheck) { + return !mDataProvider.isJustUnlockedWithFace(); + } + + return false; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java index a73ccf575249..92dd8b74e959 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE; @@ -23,11 +23,12 @@ import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.Locale; +import javax.inject.Inject; + /** * False on swipes that are too close to 45 degrees. * @@ -47,6 +48,7 @@ class DiagonalClassifier extends FalsingClassifier { private final float mHorizontalAngleRange; private final float mVerticalAngleRange; + @Inject DiagonalClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java index 524d524f38b6..50d55f6f6028 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN; @@ -27,12 +27,13 @@ import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; import java.util.Locale; +import javax.inject.Inject; + /** * Ensure that the swipe + momentum covers a minimum distance. */ @@ -54,6 +55,7 @@ class DistanceClassifier extends FalsingClassifier { private boolean mDistanceDirty; private DistanceVectors mCachedDistance; + @Inject DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java index a27ea6172414..1c8f4208edba 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DoubleTapClassifier.java @@ -14,15 +14,19 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; -import android.view.MotionEvent; +import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS; +import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TOUCH_SLOP; -import com.android.systemui.classifier.FalsingDataProvider; +import android.view.MotionEvent; import java.util.List; import java.util.Queue; +import javax.inject.Inject; +import javax.inject.Named; + /** * Returns a false touch if the most two recent gestures are not taps or are too far apart. */ @@ -34,8 +38,10 @@ public class DoubleTapClassifier extends FalsingClassifier { private StringBuilder mReason = new StringBuilder(); + @Inject DoubleTapClassifier(FalsingDataProvider dataProvider, SingleTapClassifier singleTapClassifier, - float doubleTapSlop, long doubleTapTimeMs) { + @Named(DOUBLE_TAP_TOUCH_SLOP) float doubleTapSlop, + @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs) { super(dataProvider); mSingleTapClassifier = singleTapClassifier; mDoubleTapSlop = doubleTapSlop; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java index 568dc432729f..82575c3e639e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,10 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.view.MotionEvent; -import com.android.systemui.classifier.Classifier; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.sensors.ProximitySensor; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index b29871c1c3d0..009b311f2363 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -21,9 +21,6 @@ import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import com.android.systemui.classifier.brightline.BrightLineFalsingManager; -import com.android.systemui.classifier.brightline.FalsingClassifier; -import com.android.systemui.classifier.brightline.TimeLimitedMotionEventBuffer; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.time.SystemClock; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 74629411c13d..d4f58c324d39 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -24,7 +24,6 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import com.android.systemui.Dumpable; -import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java index 937bcbaa6222..7b7f17e1568b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -16,16 +16,69 @@ package com.android.systemui.classifier; +import android.content.res.Resources; +import android.view.ViewConfiguration; + +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.statusbar.phone.NotificationTapHelper; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Named; import dagger.Binds; import dagger.Module; +import dagger.Provides; +import dagger.multibindings.ElementsIntoSet; /** Dagger Module for Falsing. */ @Module public interface FalsingModule { + String BRIGHT_LINE_GESTURE_CLASSIFERS = "bright_line_gesture_classifiers"; + String SINGLE_TAP_TOUCH_SLOP = "falsing_single_tap_touch_slop"; + String DOUBLE_TAP_TOUCH_SLOP = "falsing_double_tap_touch_slop"; + String DOUBLE_TAP_TIMEOUT_MS = "falsing_double_tap_timeout_ms"; + /** */ @Binds @SysUISingleton FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl); + + /** */ + @Provides + @ElementsIntoSet + @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) + static Set<FalsingClassifier> providesBrightLineGestureClassifiers( + DistanceClassifier distanceClassifier, ProximityClassifier proximityClassifier, + PointerCountClassifier pointerCountClassifier, TypeClassifier typeClassifier, + DiagonalClassifier diagonalClassifier, ZigZagClassifier zigZagClassifier) { + return new HashSet<>(Arrays.asList( + pointerCountClassifier, typeClassifier, diagonalClassifier, distanceClassifier, + proximityClassifier, zigZagClassifier)); + } + + /** */ + @Provides + @Named(DOUBLE_TAP_TIMEOUT_MS) + static long providesDoubleTapTimeoutMs() { + return NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS; + } + + /** */ + @Provides + @Named(DOUBLE_TAP_TOUCH_SLOP) + static float providesDoubleTapTouchSlop(@Main Resources resources) { + return resources.getDimension(R.dimen.double_tap_slop); + } + + /** */ + @Provides + @Named(SINGLE_TAP_TOUCH_SLOP) + static float providesSingleTapTouchSlop(ViewConfiguration viewConfiguration) { + return viewConfiguration.getScaledTouchSlop(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java index dd5d8a8f557b..0565165e1e8d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/PointerCountClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; - import java.util.Locale; +import javax.inject.Inject; + /** * False touch if more than one finger touches the screen. * @@ -37,6 +37,7 @@ class PointerCountClassifier extends FalsingClassifier { private static final int MAX_ALLOWED_POINTERS_SWIPE_DOWN = 2; private int mMaxPointerCount; + @Inject PointerCountClassifier(FalsingDataProvider dataProvider) { super(dataProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java index 3551c241e71e..6e73fc06de4c 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -22,12 +22,13 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.provider.DeviceConfig; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; import java.util.Locale; +import javax.inject.Inject; + /** * False touch if proximity sensor is covered for more than a certain percentage of the gesture. @@ -47,10 +48,11 @@ class ProximityClassifier extends FalsingClassifier { private long mNearDurationNs; private float mPercentNear; + @Inject ProximityClassifier(DistanceClassifier distanceClassifier, FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); - this.mDistanceClassifier = distanceClassifier; + mDistanceClassifier = distanceClassifier; mPercentCoveredThreshold = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java index 8c7648149e44..6b7a1413bc74 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/SingleTapClassifier.java @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; -import android.view.MotionEvent; +import static com.android.systemui.classifier.FalsingModule.SINGLE_TAP_TOUCH_SLOP; -import com.android.systemui.classifier.FalsingDataProvider; +import android.view.MotionEvent; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; + /** * Falsing classifier that accepts or rejects a single gesture as a tap. */ @@ -29,7 +32,9 @@ public class SingleTapClassifier extends FalsingClassifier { private final float mTouchSlop; private String mReason; - SingleTapClassifier(FalsingDataProvider dataProvider, float touchSlop) { + @Inject + SingleTapClassifier(FalsingDataProvider dataProvider, + @Named(SINGLE_TAP_TOUCH_SLOP) float touchSlop) { super(dataProvider); mTouchSlop = touchSlop; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java index 7430a1eda920..7969b4e83ac0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TimeLimitedMotionEventBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.view.MotionEvent; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java index f62871f383b3..711a0fc0f478 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; @@ -26,12 +26,13 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; -import com.android.systemui.classifier.FalsingDataProvider; +import javax.inject.Inject; /** * Ensure that the swipe direction generally matches that of the interaction type. */ public class TypeClassifier extends FalsingClassifier { + @Inject TypeClassifier(FalsingDataProvider dataProvider) { super(dataProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java index 9ca77d364bc4..383dda498b49 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE; @@ -25,13 +25,14 @@ import android.graphics.Point; import android.provider.DeviceConfig; import android.view.MotionEvent; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import javax.inject.Inject; + /** * Penalizes gestures that change direction in either the x or y too much. */ @@ -56,6 +57,7 @@ class ZigZagClassifier extends FalsingClassifier { private float mLastMaxXDeviance; private float mLastMaxYDeviance; + @Inject ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 13ff3f5c4e6c..ec4a91c24464 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -22,6 +22,7 @@ import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; import com.android.systemui.settings.brightness.BrightnessDialog; +import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.tuner.TunerActivity; import com.android.systemui.usb.UsbDebuggingActivity; import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity; @@ -85,4 +86,10 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(CreateUserActivity.class) public abstract Activity bindCreateUserActivity(CreateUserActivity activity); + + /** Inject into TvNotificationPanelActivity. */ + @Binds + @IntoMap + @ClassKey(TvNotificationPanelActivity.class) + public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 7127f26a7ed2..275bfaa1023c 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -30,7 +30,6 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; import android.os.UserHandle; -import android.util.DisplayMetrics; import android.view.Choreographer; import android.view.IWindowManager; import android.view.LayoutInflater; @@ -141,16 +140,6 @@ public class DependencyProvider { return networkController.getDataSaverController(); } - /** */ - @Provides - @SysUISingleton - public DisplayMetrics provideDisplayMetrics(Context context, WindowManager windowManager) { - DisplayMetrics displayMetrics = new DisplayMetrics(); - context.getDisplay().getMetrics(displayMetrics); - return displayMetrics; - } - - /** */ @Provides @SysUISingleton public INotificationManager provideINotificationManager() { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index c3f2e1848abd..2c06c7b96831 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -151,6 +151,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static ActivityTaskManager provideActivityTaskManager() { + return ActivityTaskManager.getInstance(); + } + + @Provides + @Singleton static IActivityTaskManager provideIActivityTaskManager() { return ActivityTaskManager.getService(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index 53383d65e379..a89c7acea984 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -16,13 +16,14 @@ package com.android.systemui.dagger; +import android.app.ActivityManager; import android.content.Context; import android.util.DisplayMetrics; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.systemui.dagger.qualifiers.TestHarness; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; -import com.android.wm.shell.animation.FlingAnimationUtils; import javax.inject.Singleton; @@ -49,15 +50,12 @@ import dagger.Provides; GlobalConcurrencyModule.class}) public class GlobalModule { - // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and - // callers should be creating a new builder on demand - @Singleton + /** */ @Provides - static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( - Context context) { + public DisplayMetrics provideDisplayMetrics(Context context) { DisplayMetrics displayMetrics = new DisplayMetrics(); context.getDisplay().getMetrics(displayMetrics); - return new FlingAnimationUtils.Builder(displayMetrics); + return displayMetrics; } /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ @@ -66,4 +64,10 @@ public class GlobalModule { static UiEventLogger provideUiEventLogger() { return new UiEventLoggerImpl(); } + + @Provides + @TestHarness + static boolean provideIsTestHarness() { + return ActivityManager.isRunningInUserTestHarness(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index c0013d8cb981..9f6c19bdf06f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -34,8 +34,8 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher; import com.android.systemui.statusbar.dagger.StatusBarModule; import com.android.systemui.statusbar.notification.InstantAppNotifier; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.tv.TvNotificationPanel; import com.android.systemui.statusbar.tv.TvStatusBar; +import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel; import com.android.systemui.theme.ThemeOverlayController; import com.android.systemui.toast.ToastUI; import com.android.systemui.util.leak.GarbageMonitor; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java new file mode 100644 index 000000000000..f68ab188c93c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/TestHarness.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + + +/** + * An annotation for injecting whether or not we are running in a test environment. + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface TestHarness { +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt index d1e5059756ed..73abf4519f73 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt @@ -21,6 +21,7 @@ import android.util.MathUtils private const val MILLIS_PER_MINUTES = 1000 * 60f private const val BURN_IN_PREVENTION_PERIOD_Y = 521f private const val BURN_IN_PREVENTION_PERIOD_X = 83f +private const val BURN_IN_PREVENTION_PERIOD_SCALE = 180f /** * Returns the translation offset that should be used to avoid burn in at @@ -36,6 +37,14 @@ fun getBurnInOffset(amplitude: Int, xAxis: Boolean): Int { } /** + * Returns a value to scale a view in order to avoid burn in. + */ +fun getBurnInScale(): Float { + return 0.8f + zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES, + 0.2f, BURN_IN_PREVENTION_PERIOD_SCALE) +} + +/** * Implements a continuous, piecewise linear, periodic zig-zag function * * Can be thought of as a linear approximation of abs(sin(x))) diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 3295595955eb..5cd3b33a1b5f 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -19,10 +19,10 @@ package com.android.systemui.people; import android.app.Activity; import android.app.INotificationManager; import android.app.people.IPeopleManager; +import android.app.people.PeopleSpaceTile; import android.content.Context; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; -import android.content.pm.ShortcutInfo; import android.os.Bundle; import android.os.ServiceManager; import android.util.Log; @@ -68,13 +68,13 @@ public class PeopleSpaceActivity extends Activity { */ private void setTileViewsWithPriorityConversations() { try { - List<Map.Entry<Long, ShortcutInfo>> shortcutInfos = PeopleSpaceUtils.getShortcutInfos( - mContext, mNotificationManager, mPeopleManager); - for (Map.Entry<Long, ShortcutInfo> entry : shortcutInfos) { - ShortcutInfo shortcutInfo = entry.getValue(); + List<Map.Entry<Long, PeopleSpaceTile>> tiles = PeopleSpaceUtils.getTiles( + mContext, mNotificationManager, mPeopleManager, mLauncherApps); + for (Map.Entry<Long, PeopleSpaceTile> entry : tiles) { + PeopleSpaceTile tile = entry.getValue(); PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout, - shortcutInfo.getId()); - setTileView(tileView, shortcutInfo, entry.getKey()); + tile.getId()); + setTileView(tileView, tile, entry.getKey()); } } catch (Exception e) { Log.e(TAG, "Couldn't retrieve conversations", e); @@ -82,18 +82,18 @@ public class PeopleSpaceActivity extends Activity { } /** Sets {@code tileView} with the data in {@code conversation}. */ - private void setTileView(PeopleSpaceTileView tileView, ShortcutInfo shortcutInfo, + private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile, long lastInteraction) { try { - String pkg = shortcutInfo.getPackage(); + String pkg = tile.getPackageName(); String status = PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction); tileView.setStatus(status); - tileView.setName(shortcutInfo.getLabel().toString()); + tileView.setName(tile.getUserName().toString()); tileView.setPackageIcon(mPackageManager.getApplicationIcon(pkg)); - tileView.setPersonIcon(mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0)); - tileView.setOnClickListener(mLauncherApps, shortcutInfo); + tileView.setPersonIcon(tile.getUserIcon()); + tileView.setOnClickListener(mLauncherApps, tile); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve shortcut information", e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java index d5ef190d5ff1..4aea5b8d6446 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java @@ -16,10 +16,12 @@ package com.android.systemui.people; +import android.app.people.PeopleSpaceTile; import android.content.Context; import android.content.pm.LauncherApps; -import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.os.UserHandle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -71,13 +73,15 @@ public class PeopleSpaceTileView extends LinearLayout { mPackageIconView.setImageDrawable(drawable); } - /** Sets the person drawable on the tile. */ - public void setPersonIcon(Drawable drawable) { - mPersonIconView.setImageDrawable(drawable); + /** Sets the person bitmap on the tile. */ + public void setPersonIcon(Icon icon) { + mPersonIconView.setImageIcon(icon); } /** Sets the click listener of the tile. */ - public void setOnClickListener(LauncherApps launcherApps, ShortcutInfo shortcutInfo) { - mTileView.setOnClickListener(v -> launcherApps.startShortcut(shortcutInfo, null, null)); + public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) { + mTileView.setOnClickListener(v -> + launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null, + UserHandle.getUserHandleForUid(tile.getUid()))); } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java index 35e445b968ba..fe262b446a3c 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceUtils.java @@ -19,8 +19,9 @@ package com.android.systemui.people; import android.app.INotificationManager; import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; +import android.app.people.PeopleSpaceTile; import android.content.Context; -import android.content.pm.ShortcutInfo; +import android.content.pm.LauncherApps; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; @@ -51,30 +52,34 @@ public class PeopleSpaceUtils { private static final int MIN_HOUR = 1; private static final int ONE_DAY = 1; - /** Returns a list of map entries corresponding to user's conversations. */ - public static List<Map.Entry<Long, ShortcutInfo>> getShortcutInfos(Context context, - INotificationManager notificationManager, IPeopleManager peopleManager) + public static List<Map.Entry<Long, PeopleSpaceTile>> getTiles( + Context context, INotificationManager notificationManager, IPeopleManager peopleManager, + LauncherApps launcherApps) throws Exception { boolean showAllConversations = Settings.Global.getInt(context.getContentResolver(), Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, 0) == 0; List<ConversationChannelWrapper> conversations = notificationManager.getConversations( true).getList(); - List<Map.Entry<Long, ShortcutInfo>> shortcutInfos = getSortedShortcutInfos(peopleManager, - conversations.stream().map(c -> c.getShortcutInfo())); + List<Map.Entry<Long, PeopleSpaceTile>> tiles = getSortedTiles(peopleManager, + conversations.stream().map(c -> + new PeopleSpaceTile.Builder(c.getShortcutInfo(), launcherApps).build())); if (showAllConversations) { List<ConversationChannel> recentConversations = peopleManager.getRecentConversations().getList(); - List<Map.Entry<Long, ShortcutInfo>> recentShortcutInfos = getSortedShortcutInfos( - peopleManager, recentConversations.stream().map(c -> c.getShortcutInfo())); - shortcutInfos.addAll(recentShortcutInfos); + List<Map.Entry<Long, PeopleSpaceTile>> recentTiles = + getSortedTiles(peopleManager, recentConversations.stream().map(c -> + new PeopleSpaceTile + .Builder(c.getShortcutInfo(), launcherApps) + .build())); + tiles.addAll(recentTiles); } - return shortcutInfos; + return tiles; } /** Returns a list sorted by ascending last interaction time from {@code stream}. */ - private static List<Map.Entry<Long, ShortcutInfo>> getSortedShortcutInfos( - IPeopleManager peopleManager, Stream<ShortcutInfo> stream) { + private static List<Map.Entry<Long, PeopleSpaceTile>> getSortedTiles( + IPeopleManager peopleManager, Stream<PeopleSpaceTile> stream) { return stream .filter(c -> shouldKeepConversation(c)) .map(c -> Map.entry(getLastInteraction(peopleManager, c), c)) @@ -82,13 +87,13 @@ public class PeopleSpaceUtils { .collect(Collectors.toList()); } - /** Returns the last interaction time with the user specified by {@code shortcutInfo}. */ + /** Returns the last interaction time with the user specified by {@code PeopleSpaceTile}. */ private static Long getLastInteraction(IPeopleManager peopleManager, - ShortcutInfo shortcutInfo) { + PeopleSpaceTile tile) { try { - int userId = UserHandle.getUserHandleForUid(shortcutInfo.getUserId()).getIdentifier(); - String pkg = shortcutInfo.getPackage(); - return peopleManager.getLastInteraction(pkg, userId, shortcutInfo.getId()); + int userId = UserHandle.getUserHandleForUid(tile.getUid()).getIdentifier(); + String pkg = tile.getPackageName(); + return peopleManager.getLastInteraction(pkg, userId, tile.getId()); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve last interaction time", e); return 0L; @@ -157,13 +162,13 @@ public class PeopleSpaceUtils { * * <p>A valid {@code conversation} must: * <ul> - * <li>Have a non-null {@link ShortcutInfo} - * <li>Have an associated label in the {@link ShortcutInfo} + * <li>Have a non-null {@link PeopleSpaceTile} + * <li>Have an associated label in the {@link PeopleSpaceTile} * </ul> * </li> */ - public static boolean shouldKeepConversation(ShortcutInfo shortcutInfo) { - return shortcutInfo != null && shortcutInfo.getLabel().length() != 0; + public static boolean shouldKeepConversation(PeopleSpaceTile tile) { + return tile != null && tile.getUserName().length() != 0; } } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index 44f173bc5175..0b0308f7dcda 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -19,8 +19,8 @@ package com.android.systemui.people.widget; import android.app.Activity; import android.content.Intent; import android.content.pm.LauncherApps; -import android.content.pm.ShortcutInfo; import android.os.Bundle; +import android.os.UserHandle; import android.util.Log; import com.android.systemui.people.PeopleSpaceUtils; @@ -36,18 +36,19 @@ public class LaunchConversationActivity extends Activity { if (DEBUG) Log.d(TAG, "onCreate called"); Intent intent = getIntent(); - ShortcutInfo shortcutInfo = (ShortcutInfo) intent.getParcelableExtra( - PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO - ); - if (shortcutInfo != null) { + String tileId = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID); + String packageName = intent.getStringExtra(PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME); + int uid = intent.getIntExtra(PeopleSpaceWidgetProvider.EXTRA_UID, 0); + + if (tileId != null && !tileId.isEmpty()) { if (DEBUG) { - Log.d(TAG, "Launching conversation with shortcutInfo id " + shortcutInfo.getId()); + Log.d(TAG, "Launching conversation with shortcutInfo id " + tileId); } try { LauncherApps launcherApps = getApplicationContext().getSystemService(LauncherApps.class); launcherApps.startShortcut( - shortcutInfo, null, null); + packageName, tileId, null, null, UserHandle.getUserHandleForUid(uid)); } catch (Exception e) { Log.e(TAG, "Exception starting shortcut:" + e); } diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java index aa98b61ff947..9f84514f8c47 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetProvider.java @@ -32,7 +32,9 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider { private static final String TAG = "PeopleSpaceWidgetPvd"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; - public static final String EXTRA_SHORTCUT_INFO = "extra_shortcut_info"; + public static final String EXTRA_TILE_ID = "extra_tile_id"; + public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; + public static final String EXTRA_UID = "extra_uid"; /** Called when widget updates. */ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java index a4527c3ef86f..1e6c213db852 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetRemoteViewsFactory.java @@ -18,11 +18,11 @@ package com.android.systemui.people.widget; import android.app.INotificationManager; import android.app.people.IPeopleManager; +import android.app.people.PeopleSpaceTile; import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; -import android.content.pm.ShortcutInfo; import android.os.ServiceManager; import android.util.Log; import android.widget.RemoteViews; @@ -45,7 +45,7 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R private INotificationManager mNotificationManager; private PackageManager mPackageManager; private LauncherApps mLauncherApps; - private List<Map.Entry<Long, ShortcutInfo>> mShortcutInfos = new ArrayList<>(); + private List<Map.Entry<Long, PeopleSpaceTile>> mTiles = new ArrayList<>(); private Context mContext; public PeopleSpaceWidgetRemoteViewsFactory(Context context, Intent intent) { @@ -70,8 +70,8 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R */ private void setTileViewsWithPriorityConversations() { try { - mShortcutInfos = PeopleSpaceUtils.getShortcutInfos(mContext, mNotificationManager, - mPeopleManager); + mTiles = PeopleSpaceUtils.getTiles(mContext, mNotificationManager, + mPeopleManager, mLauncherApps); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve conversations", e); } @@ -85,12 +85,12 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R @Override public void onDestroy() { - mShortcutInfos.clear(); + mTiles.clear(); } @Override public int getCount() { - return mShortcutInfos.size(); + return mTiles.size(); } @Override @@ -100,30 +100,28 @@ public class PeopleSpaceWidgetRemoteViewsFactory implements RemoteViewsService.R RemoteViews personView = new RemoteViews(mContext.getPackageName(), R.layout.people_space_widget_item); try { - Map.Entry<Long, ShortcutInfo> entry = mShortcutInfos.get(i); - ShortcutInfo shortcutInfo = entry.getValue(); + Map.Entry<Long, PeopleSpaceTile> entry = mTiles.get(i); + PeopleSpaceTile tile = entry.getValue(); long lastInteraction = entry.getKey(); String status = PeopleSpaceUtils.getLastInteractionString(mContext, lastInteraction); personView.setTextViewText(R.id.status, status); - personView.setTextViewText(R.id.name, shortcutInfo.getLabel().toString()); + personView.setTextViewText(R.id.name, tile.getUserName().toString()); personView.setImageViewBitmap( R.id.package_icon, PeopleSpaceUtils.convertDrawableToBitmap( - mPackageManager.getApplicationIcon(shortcutInfo.getPackage()) - ) - ); - personView.setImageViewBitmap( - R.id.person_icon, - PeopleSpaceUtils.convertDrawableToBitmap( - mLauncherApps.getShortcutIconDrawable(shortcutInfo, 0) + mPackageManager.getApplicationIcon(tile.getPackageName()) ) ); + personView.setImageViewIcon(R.id.person_icon, tile.getUserIcon()); Intent fillInIntent = new Intent(); - fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_SHORTCUT_INFO, shortcutInfo); + fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_TILE_ID, tile.getId()); + fillInIntent.putExtra( + PeopleSpaceWidgetProvider.EXTRA_PACKAGE_NAME, tile.getPackageName()); + fillInIntent.putExtra(PeopleSpaceWidgetProvider.EXTRA_UID, tile.getUid()); personView.setOnClickFillInIntent(R.id.item, fillInIntent); } catch (Exception e) { Log.e(TAG, "Couldn't retrieve shortcut information", e); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 478923994af8..5afe526be918 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -68,6 +68,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen private final View mRootView; private final TextView mFooterText; private final ImageView mFooterIcon; + private final ImageView mPrimaryFooterIcon; private final Context mContext; private final Callback mCallback = new Callback(); private final SecurityController mSecurityController; @@ -83,6 +84,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen private CharSequence mFooterTextContent = null; private int mFooterTextId; private int mFooterIconId; + private Drawable mPrimaryFooterIconDrawable; @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, Context context, @@ -92,6 +94,7 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen mRootView.setOnClickListener(this); mFooterText = mRootView.findViewById(R.id.footer_text); mFooterIcon = mRootView.findViewById(R.id.footer_icon); + mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooterIconId = R.drawable.ic_info_outline; mContext = context; mMainHandler = mainHandler; @@ -188,6 +191,18 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen mFooterIconId = footerIconId; mMainHandler.post(mUpdateIcon); } + + // Update the primary icon + if (isParentalControlsEnabled) { + if (mPrimaryFooterIconDrawable == null) { + DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo(); + mPrimaryFooterIconDrawable = mSecurityController.getIcon(info); + } + } else { + mPrimaryFooterIconDrawable = null; + } + mMainHandler.post(mUpdatePrimaryIcon); + mMainHandler.post(mUpdateDisplayState); } @@ -532,6 +547,15 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen } }; + private final Runnable mUpdatePrimaryIcon = new Runnable() { + @Override + public void run() { + mPrimaryFooterIcon.setVisibility(mPrimaryFooterIconDrawable != null + ? View.VISIBLE : View.GONE); + mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable); + } + }; + private final Runnable mUpdateDisplayState = new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 35874cd8bb28..d7f9c6163b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -16,20 +16,20 @@ package com.android.systemui.screenshot; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; import static java.util.Objects.requireNonNull; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.app.Notification; import android.content.ComponentName; import android.content.Context; -import android.content.res.Configuration; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.PixelFormat; @@ -52,7 +52,8 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; -import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; @@ -60,6 +61,8 @@ import android.widget.Toast; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.UiEventLogger; +import com.android.internal.policy.PhoneWindow; +import com.android.settingslib.applications.InterestingConfigChanges; import com.android.systemui.R; import com.android.systemui.util.DeviceConfigProxy; @@ -141,12 +144,13 @@ public class ScreenshotController { private final WindowManager mWindowManager; private final WindowManager.LayoutParams mWindowLayoutParams; - private final Display mDisplay; private final DisplayMetrics mDisplayMetrics; private final AccessibilityManager mAccessibilityManager; private final MediaActionSound mCameraSound; private final ScrollCaptureClient mScrollCaptureClient; private final DeviceConfigProxy mConfigProxy; + private final PhoneWindow mWindow; + private final View mDecorView; private final Binder mWindowToken; private ScreenshotView mScreenshotView; @@ -156,9 +160,6 @@ public class ScreenshotController { private Animator mScreenshotAnimation; private Runnable mOnCompleteRunnable; - private boolean mInDarkMode; - private boolean mDirectionLTR; - private boolean mOrientationPortrait; private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) { @Override @@ -174,6 +175,15 @@ public class ScreenshotController { } }; + /** Tracks config changes that require re-creating UI */ + private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( + ActivityInfo.CONFIG_ORIENTATION + | ActivityInfo.CONFIG_LAYOUT_DIRECTION + | ActivityInfo.CONFIG_LOCALE + | ActivityInfo.CONFIG_UI_MODE + | ActivityInfo.CONFIG_SCREEN_LAYOUT + | ActivityInfo.CONFIG_ASSETS_PATHS); + @Inject ScreenshotController( Context context, @@ -188,25 +198,20 @@ public class ScreenshotController { mUiEventLogger = uiEventLogger; final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class)); - mDisplay = dm.getDisplay(DEFAULT_DISPLAY); - mContext = context.createDisplayContext(mDisplay); + final Display display = dm.getDisplay(DEFAULT_DISPLAY); + final Context displayContext = context.createDisplayContext(display); + mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null); mWindowManager = mContext.getSystemService(WindowManager.class); mAccessibilityManager = AccessibilityManager.getInstance(mContext); mConfigProxy = configProxy; - reloadAssets(); - Configuration config = mContext.getResources().getConfiguration(); - mInDarkMode = config.isNightModeActive(); - mDirectionLTR = config.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; - mOrientationPortrait = config.orientation == ORIENTATION_PORTRAIT; mWindowToken = new Binder("ScreenshotController"); mScrollCaptureClient.setHostWindowToken(mWindowToken); // Setup the window that we are going to use mWindowLayoutParams = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, - WindowManager.LayoutParams.TYPE_SCREENSHOT, + MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT, WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL @@ -217,13 +222,21 @@ public class ScreenshotController { mWindowLayoutParams.setTitle("ScreenshotAnimation"); mWindowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - mWindowLayoutParams.setFitInsetsTypes(0 /* types */); mWindowLayoutParams.token = mWindowToken; // This is needed to let touches pass through outside the touchable areas mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + mWindow = new PhoneWindow(mContext); + mWindow.setWindowManager(mWindowManager, null, null); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS); + mWindow.setBackgroundDrawableResource(android.R.color.transparent); + mDecorView = mWindow.getDecorView(); + + reloadAssets(); + mDisplayMetrics = new DisplayMetrics(); - mDisplay.getRealMetrics(mDisplayMetrics); + display.getRealMetrics(mDisplayMetrics); // Setup the Camera shutter sound mCameraSound = new MediaActionSound(); @@ -233,7 +246,6 @@ public class ScreenshotController { void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) { mOnCompleteRunnable = onComplete; - mDisplay.getRealMetrics(mDisplayMetrics); takeScreenshotInternal( finisher, new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); @@ -254,19 +266,18 @@ public class ScreenshotController { return; } - if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) { - saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false); - } else { - saveScreenshot(screenshot, finisher, - new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE, - true); + boolean showFlash = false; + if (!aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) { + showFlash = true; + visibleInsets = Insets.NONE; + screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight()); } + saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash); } /** * Displays a screenshot selector */ - @SuppressLint("ClickableViewAccessibility") void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) { dismissScreenshot(true); mOnCompleteRunnable = onComplete; @@ -296,70 +307,27 @@ public class ScreenshotController { } } - private void onConfigChanged(Configuration newConfig) { - boolean needsUpdate = false; - // dark mode - if (newConfig.isNightModeActive()) { - // Night mode is active, we're using dark theme - if (!mInDarkMode) { - mInDarkMode = true; - needsUpdate = true; - } - } else { - // Night mode is not active, we're using the light theme - if (mInDarkMode) { - mInDarkMode = false; - needsUpdate = true; - } - } - - // RTL configuration - switch (newConfig.getLayoutDirection()) { - case View.LAYOUT_DIRECTION_LTR: - if (!mDirectionLTR) { - mDirectionLTR = true; - needsUpdate = true; - } - break; - case View.LAYOUT_DIRECTION_RTL: - if (mDirectionLTR) { - mDirectionLTR = false; - needsUpdate = true; - } - break; - } - - // portrait/landscape orientation - switch (newConfig.orientation) { - case ORIENTATION_PORTRAIT: - if (!mOrientationPortrait) { - mOrientationPortrait = true; - needsUpdate = true; - } - break; - case ORIENTATION_LANDSCAPE: - if (mOrientationPortrait) { - mOrientationPortrait = false; - needsUpdate = true; - } - break; - } - - if (needsUpdate) { - reloadAssets(); - } - } - /** * Update assets (called when the dark theme status changes). We only need to update the dismiss * button and the actions container background, since the buttons are re-inflated on demand. */ private void reloadAssets() { - boolean wasAttached = mScreenshotView != null && mScreenshotView.isAttachedToWindow(); + boolean wasAttached = mDecorView.isAttachedToWindow(); if (wasAttached) { - mWindowManager.removeView(mScreenshotView); + mWindowManager.removeView(mDecorView); } + // respect the display cutout in landscape (since we'd otherwise overlap) but not portrait + int orientation = mContext.getResources().getConfiguration().orientation; + mWindowLayoutParams.setFitInsetsTypes( + orientation == ORIENTATION_PORTRAIT ? 0 : WindowInsets.Type.displayCutout()); + + // ignore system bar insets for the purpose of window layout + mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets( + new WindowInsets.Builder(insets) + .setInsets(WindowInsets.Type.all(), Insets.NONE) + .build())); + // Inflate the screenshot layout mScreenshotView = (ScreenshotView) LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null); @@ -382,9 +350,8 @@ public class ScreenshotController { return false; }); - if (wasAttached) { - mWindowManager.addView(mScreenshotView, mWindowLayoutParams); - } + // view is added to window manager in startAnimation + mWindow.setContentView(mScreenshotView, mWindowLayoutParams); } /** @@ -448,7 +415,9 @@ public class ScreenshotController { mScreenBitmap.setHasAlpha(false); mScreenBitmap.prepareToDraw(); - onConfigChanged(mContext.getResources().getConfiguration()); + if (mConfigChanges.applyNewConfig(mContext.getResources())) { + reloadAssets(); + } // The window is focusable by default setWindowFocusable(true); @@ -510,10 +479,10 @@ public class ScreenshotController { mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); mScreenshotHandler.post(() -> { if (!mScreenshotView.isAttachedToWindow()) { - mWindowManager.addView(mScreenshotView, mWindowLayoutParams); + mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams); } - mScreenshotView.prepareForAnimation(mScreenBitmap, screenRect, screenInsets); + mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets); mScreenshotHandler.post(() -> { mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener( @@ -541,7 +510,7 @@ public class ScreenshotController { private void resetScreenshotView() { if (mScreenshotView.isAttachedToWindow()) { - mWindowManager.removeView(mScreenshotView); + mWindowManager.removeView(mDecorView); } mScreenshotView.reset(); mOnCompleteRunnable.run(); @@ -636,8 +605,8 @@ public class ScreenshotController { } else { mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } - if (mScreenshotView.isAttachedToWindow()) { - mWindowManager.updateViewLayout(mScreenshotView, mWindowLayoutParams); + if (mDecorView.isAttachedToWindow()) { + mWindowManager.updateViewLayout(mDecorView, mWindowLayoutParams); } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index b020275ae3ae..3814bd2999e9 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -240,8 +240,7 @@ public class ScreenshotView extends FrameLayout implements setOnApplyWindowInsetsListener((v, insets) -> { if (QuickStepContract.isGesturalMode(mNavMode)) { - Insets gestureInsets = insets.getInsets( - WindowInsets.Type.systemGestures()); + Insets gestureInsets = insets.getInsets(WindowInsets.Type.systemGestures()); mLeftInset = gestureInsets.left; mRightInset = gestureInsets.right; } else { @@ -272,7 +271,7 @@ public class ScreenshotView extends FrameLayout implements mScreenshotSelectorView.requestFocus(); } - void prepareForAnimation(Bitmap bitmap, Rect screenRect, Insets screenInsets) { + void prepareForAnimation(Bitmap bitmap, Insets screenInsets) { mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets)); // make static preview invisible (from gone) so we can query its location on screen mScreenshotPreview.setVisibility(View.INVISIBLE); @@ -284,6 +283,8 @@ public class ScreenshotView extends FrameLayout implements Rect previewBounds = new Rect(); mScreenshotPreview.getBoundsOnScreen(previewBounds); + int[] previewLocation = new int[2]; + mScreenshotPreview.getLocationInWindow(previewLocation); float cornerScale = mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height()); @@ -310,7 +311,8 @@ public class ScreenshotView extends FrameLayout implements // animate from the current location, to the static preview location final PointF startPos = new PointF(bounds.centerX(), bounds.centerY()); - final PointF finalPos = new PointF(previewBounds.centerX(), previewBounds.centerY()); + final PointF finalPos = new PointF(previewLocation[0] + previewBounds.width() / 2f, + previewLocation[1] + previewBounds.height() / 2f); ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1); toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 2c82bcb24ad0..4aaea8041abd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -41,6 +41,8 @@ import android.os.UserManager; import android.util.Log; import android.view.WindowManager; +import androidx.annotation.NonNull; + import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; import com.android.systemui.R; @@ -57,9 +59,8 @@ public class TakeScreenshotService extends Service { private final UserManager mUserManager; private final UiEventLogger mUiEventLogger; private final ScreenshotNotificationsController mNotificationsController; - - private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { - + private final Handler mHandler; + private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) { @@ -68,72 +69,11 @@ public class TakeScreenshotService extends Service { } }; - private Handler mHandler = new Handler(Looper.myLooper()) { - @Override - public void handleMessage(Message msg) { - final Messenger callback = msg.replyTo; - Consumer<Uri> uriConsumer = uri -> { - Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri); - try { - callback.send(reply); - } catch (RemoteException e) { - } - }; - Runnable onComplete = () -> { - Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE); - try { - callback.send(reply); - } catch (RemoteException e) { - } - }; - - // If the storage for this user is locked, we have no place to store - // the screenshot, so skip taking it instead of showing a misleading - // animation and error notification. - if (!mUserManager.isUserUnlocked()) { - Log.w(TAG, "Skipping screenshot because storage is locked!"); - mNotificationsController.notifyScreenshotError( - R.string.screenshot_failed_to_save_user_locked_text); - post(() -> uriConsumer.accept(null)); - post(onComplete); - return; - } - - ScreenshotHelper.ScreenshotRequest screenshotRequest = - (ScreenshotHelper.ScreenshotRequest) msg.obj; - - mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); - - switch (msg.what) { - case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: - mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete); - break; - case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: - mScreenshot.takeScreenshotPartial(uriConsumer, onComplete); - break; - case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: - Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( - screenshotRequest.getBitmapBundle()); - Rect screenBounds = screenshotRequest.getBoundsInScreen(); - Insets insets = screenshotRequest.getInsets(); - int taskId = screenshotRequest.getTaskId(); - int userId = screenshotRequest.getUserId(); - ComponentName topComponent = screenshotRequest.getTopComponent(); - mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, - taskId, userId, topComponent, uriConsumer, onComplete); - break; - default: - Log.d(TAG, "Invalid screenshot option: " + msg.what); - } - } - }; - @Inject - public TakeScreenshotService( - ScreenshotController screenshotController, - UserManager userManager, + public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController) { + mHandler = new Handler(Looper.getMainLooper(), this::handleMessage); mScreenshot = screenshotController; mUserManager = userManager; mUiEventLogger = uiEventLogger; @@ -141,13 +81,9 @@ public class TakeScreenshotService extends Service { } @Override - public IBinder onBind(Intent intent) { - // register broadcast receiver - IntentFilter filter = new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS); - registerReceiver(mBroadcastReceiver, filter); - + public IBinder onBind(@NonNull Intent intent) { + registerReceiver(mCloseSystemDialogs, new IntentFilter(ACTION_CLOSE_SYSTEM_DIALOGS)); return new Messenger(mHandler).getBinder(); - } @Override @@ -155,7 +91,73 @@ public class TakeScreenshotService extends Service { if (mScreenshot != null) { mScreenshot.dismissScreenshot(true); } - unregisterReceiver(mBroadcastReceiver); + unregisterReceiver(mCloseSystemDialogs); + return false; + } + + /** Respond to incoming Message via Binder (Messenger) */ + private boolean handleMessage(Message msg) { + final Messenger replyTo = msg.replyTo; + final Runnable onComplete = () -> sendComplete(replyTo); + final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri); + + // If the storage for this user is locked, we have no place to store + // the screenshot, so skip taking it instead of showing a misleading + // animation and error notification. + if (!mUserManager.isUserUnlocked()) { + Log.w(TAG, "Skipping screenshot because storage is locked!"); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_user_locked_text); + uriConsumer.accept(null); + onComplete.run(); + return true; + } + + ScreenshotHelper.ScreenshotRequest screenshotRequest = + (ScreenshotHelper.ScreenshotRequest) msg.obj; + + mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource())); + + switch (msg.what) { + case WindowManager.TAKE_SCREENSHOT_FULLSCREEN: + mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete); + break; + case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION: + mScreenshot.takeScreenshotPartial(uriConsumer, onComplete); + break; + case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE: + Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap( + screenshotRequest.getBitmapBundle()); + Rect screenBounds = screenshotRequest.getBoundsInScreen(); + Insets insets = screenshotRequest.getInsets(); + int taskId = screenshotRequest.getTaskId(); + int userId = screenshotRequest.getUserId(); + ComponentName topComponent = screenshotRequest.getTopComponent(); + mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets, + taskId, userId, topComponent, uriConsumer, onComplete); + break; + default: + Log.w(TAG, "Invalid screenshot option: " + msg.what); + return false; + } return true; + }; + + private void sendComplete(Messenger target) { + try { + Log.d(TAG, "sendComplete: " + target); + target.send(Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE)); + } catch (RemoteException e) { + Log.d(TAG, "ignored remote exception", e); + } + } + + private void reportUri(Messenger target, Uri uri) { + try { + Log.d(TAG, "reportUri: " + target + " -> " + uri); + target.send(Message.obtain(null, SCREENSHOT_MSG_URI, uri)); + } catch (RemoteException e) { + Log.d(TAG, "ignored remote exception", e); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index a252a7a12274..3765e5a26e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -69,7 +69,6 @@ import com.android.systemui.util.wakelock.WakeLock; import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.NumberFormat; -import java.util.IllegalFormatConversionException; import javax.inject.Inject; @@ -575,24 +574,12 @@ public class KeyguardIndicationController implements StateListener, String percentage = NumberFormat.getPercentInstance() .format(mBatteryLevel / 100f); if (hasChargingTime) { - // We now have battery percentage in these strings and it's expected that all - // locales will also have it in the future. For now, we still have to support the old - // format until all languages get the new translations. String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( mContext, mChargingTimeRemaining); - try { - return mContext.getResources().getString(chargingId, chargingTimeFormatted, - percentage); - } catch (IllegalFormatConversionException e) { - return mContext.getResources().getString(chargingId, chargingTimeFormatted); - } + return mContext.getResources().getString(chargingId, chargingTimeFormatted, + percentage); } else { - // Same as above - try { - return mContext.getResources().getString(chargingId, percentage); - } catch (IllegalFormatConversionException e) { - return mContext.getResources().getString(chargingId); - } + return mContext.getResources().getString(chargingId, percentage); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 84818eea8a23..dbee0ee8f5d5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,19 +11,22 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.systemui.statusbar; import android.app.Notification; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.text.TextUtils; +import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.R; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.ConversationLayout; import com.android.internal.widget.NotificationExpandButton; @@ -36,35 +39,38 @@ import java.util.List; import java.util.Objects; /** - * A Util to manage {@link android.view.NotificationHeaderView} objects and their redundancies. + * A utility to manage notification views when they are placed in a group by adjusting elements + * to reduce redundancies and occasionally tweak layouts to highlight the unique content. */ -public class NotificationHeaderUtil { - - private static final TextViewComparator sTextViewComparator = new TextViewComparator(); - private static final TextViewComparator sAppNameComparator = new AppNameComparator(); - private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator(); - private static final VisibilityApplicator sAppNameApplicator = new AppNameApplicator(); - private static final DataExtractor sIconExtractor = new DataExtractor() { +public class NotificationGroupingUtil { + + private static final TextViewComparator TEXT_VIEW_COMPARATOR = new TextViewComparator(); + private static final TextViewComparator APP_NAME_COMPARATOR = new AppNameComparator(); + private static final ViewComparator BADGE_COMPARATOR = new BadgeComparator(); + private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator(); + private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator(); + private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator(); + private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() { @Override public Object extractData(ExpandableNotificationRow row) { return row.getEntry().getSbn().getNotification(); } }; - private static final IconComparator sIconVisibilityComparator = new IconComparator() { + private static final IconComparator ICON_VISIBILITY_COMPARATOR = new IconComparator() { public boolean compare(View parent, View child, Object parentData, Object childData) { return hasSameIcon(parentData, childData) && hasSameColor(parentData, childData); } }; - private static final IconComparator sGreyComparator = new IconComparator() { + private static final IconComparator GREY_COMPARATOR = new IconComparator() { public boolean compare(View parent, View child, Object parentData, Object childData) { return !hasSameIcon(parentData, childData) || hasSameColor(parentData, childData); } }; - private final static ResultApplicator mGreyApplicator = new ResultApplicator() { + private static final ResultApplicator GREY_APPLICATOR = new ResultApplicator() { @Override public void apply(View parent, View view, boolean apply, boolean reset) { CachingIconView icon = view.findViewById(com.android.internal.R.id.icon); @@ -80,87 +86,79 @@ public class NotificationHeaderUtil { }; private final ExpandableNotificationRow mRow; - private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>(); + private final ArrayList<Processor> mProcessors = new ArrayList<>(); private final HashSet<Integer> mDividers = new HashSet<>(); - public NotificationHeaderUtil(ExpandableNotificationRow row) { + public NotificationGroupingUtil(ExpandableNotificationRow row) { mRow = row; // To hide the icons if they are the same and the color is the same - mComparators.add(new HeaderProcessor(mRow, + mProcessors.add(new Processor(mRow, com.android.internal.R.id.icon, - sIconExtractor, - sIconVisibilityComparator, - sVisibilityApplicator)); + ICON_EXTRACTOR, + ICON_VISIBILITY_COMPARATOR, + VISIBILITY_APPLICATOR)); // To grey them out the icons and expand button when the icons are not the same - mComparators.add(new HeaderProcessor(mRow, - com.android.internal.R.id.notification_header, - sIconExtractor, - sGreyComparator, - mGreyApplicator)); - mComparators.add(new HeaderProcessor(mRow, + mProcessors.add(new Processor(mRow, + com.android.internal.R.id.status_bar_latest_event_content, + ICON_EXTRACTOR, + GREY_COMPARATOR, + GREY_APPLICATOR)); + mProcessors.add(new Processor(mRow, + com.android.internal.R.id.status_bar_latest_event_content, + ICON_EXTRACTOR, + ICON_VISIBILITY_COMPARATOR, + LEFT_ICON_APPLICATOR)); + mProcessors.add(new Processor(mRow, com.android.internal.R.id.profile_badge, null /* Extractor */, - new ViewComparator() { - @Override - public boolean compare(View parent, View child, Object parentData, - Object childData) { - return parent.getVisibility() != View.GONE; - } - - @Override - public boolean isEmpty(View view) { - if (view instanceof ImageView) { - return ((ImageView) view).getDrawable() == null; - } - return false; - } - }, - sVisibilityApplicator)); - mComparators.add(new HeaderProcessor( - mRow, + BADGE_COMPARATOR, + VISIBILITY_APPLICATOR)); + mProcessors.add(new Processor(mRow, com.android.internal.R.id.app_name_text, null, - sAppNameComparator, - sAppNameApplicator)); - mComparators.add(HeaderProcessor.forTextView(mRow, - com.android.internal.R.id.header_text)); + APP_NAME_COMPARATOR, + APP_NAME_APPLICATOR)); + mProcessors.add(Processor.forTextView(mRow, com.android.internal.R.id.header_text)); mDividers.add(com.android.internal.R.id.header_text_divider); mDividers.add(com.android.internal.R.id.header_text_secondary_divider); mDividers.add(com.android.internal.R.id.time_divider); } - public void updateChildrenHeaderAppearance() { + /** + * Update the appearance of the children in this group to reduce redundancies. + */ + public void updateChildrenAppearance() { List<ExpandableNotificationRow> notificationChildren = mRow.getAttachedChildren(); - if (notificationChildren == null) { + if (notificationChildren == null || !mRow.isSummaryWithChildren()) { return; } - // Initialize the comparators - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).init(); + // Initialize the processors + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).init(); } // Compare all notification headers for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow row = notificationChildren.get(i); - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).compareToHeader(row); + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).compareToGroupParent(row); } } // Apply the comparison to the row for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow row = notificationChildren.get(i); - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).apply(row); + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).apply(row); } // We need to sanitize the dividers since they might be off-balance now - sanitizeHeaderViews(row); + sanitizeTopLineViews(row); } } - private void sanitizeHeaderViews(ExpandableNotificationRow row) { + private void sanitizeTopLineViews(ExpandableNotificationRow row) { if (row.isSummaryWithChildren()) { - sanitizeHeader(row.getNotificationViewWrapper().getNotificationHeader()); + sanitizeTopLine(row.getNotificationViewWrapper().getNotificationHeader()); return; } final NotificationContentView layout = row.getPrivateLayout(); @@ -171,13 +169,11 @@ public class NotificationHeaderUtil { private void sanitizeChild(View child) { if (child != null) { - ViewGroup header = child.findViewById( - com.android.internal.R.id.notification_top_line); - sanitizeHeader(header); + sanitizeTopLine(child.findViewById(R.id.notification_top_line)); } } - private void sanitizeHeader(ViewGroup rowHeader) { + private void sanitizeTopLine(ViewGroup rowHeader) { if (rowHeader == null) { return; } @@ -225,28 +221,31 @@ public class NotificationHeaderUtil { } } - public void restoreNotificationHeader(ExpandableNotificationRow row) { - for (int compI = 0; compI < mComparators.size(); compI++) { - mComparators.get(compI).apply(row, true /* reset */); + /** + * Reset the modifications to this row for removing it from the group. + */ + public void restoreChildNotification(ExpandableNotificationRow row) { + for (int compI = 0; compI < mProcessors.size(); compI++) { + mProcessors.get(compI).apply(row, true /* reset */); } - sanitizeHeaderViews(row); + sanitizeTopLineViews(row); } - private static class HeaderProcessor { + private static class Processor { private final int mId; private final DataExtractor mExtractor; + private final ViewComparator mComparator; private final ResultApplicator mApplicator; private final ExpandableNotificationRow mParentRow; private boolean mApply; private View mParentView; - private ViewComparator mComparator; private Object mParentData; - public static HeaderProcessor forTextView(ExpandableNotificationRow row, int id) { - return new HeaderProcessor(row, id, null, sTextViewComparator, sVisibilityApplicator); + public static Processor forTextView(ExpandableNotificationRow row, int id) { + return new Processor(row, id, null, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR); } - HeaderProcessor(ExpandableNotificationRow row, int id, DataExtractor extractor, + Processor(ExpandableNotificationRow row, int id, DataExtractor extractor, ViewComparator comparator, ResultApplicator applicator) { mId = id; @@ -257,12 +256,12 @@ public class NotificationHeaderUtil { } public void init() { - mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader() - .findViewById(mId); + View header = mParentRow.getNotificationViewWrapper().getNotificationHeader(); + mParentView = header == null ? null : header.findViewById(mId); mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow); mApply = !mComparator.isEmpty(mParentView); } - public void compareToHeader(ExpandableNotificationRow row) { + public void compareToGroupParent(ExpandableNotificationRow row) { if (!mApply) { return; } @@ -308,8 +307,8 @@ public class NotificationHeaderUtil { private interface ViewComparator { /** - * @param parent the parent view - * @param child the child view + * @param parent the view with the given id in the group header + * @param child the view with the given id in the child notification * @param parentData optional data for the parent * @param childData optional data for the child * @return whether to views are the same @@ -322,6 +321,21 @@ public class NotificationHeaderUtil { Object extractData(ExpandableNotificationRow row); } + private static class BadgeComparator implements ViewComparator { + @Override + public boolean compare(View parent, View child, Object parentData, Object childData) { + return parent.getVisibility() != View.GONE; + } + + @Override + public boolean isEmpty(View view) { + if (view instanceof ImageView) { + return ((ImageView) view).getDrawable() == null; + } + return false; + } + } + private static class TextViewComparator implements ViewComparator { @Override public boolean compare(View parent, View child, Object parentData, Object childData) { @@ -338,7 +352,7 @@ public class NotificationHeaderUtil { } } - private static abstract class IconComparator implements ViewComparator { + private abstract static class IconComparator implements ViewComparator { @Override public boolean compare(View parent, View child, Object parentData, Object childData) { return false; @@ -366,6 +380,12 @@ public class NotificationHeaderUtil { } private interface ResultApplicator { + /** + * @param parent the root view of the child notification + * @param view the view with the given id in the child notification + * @param apply whether the state should be applied or removed + * @param reset if [de]application is the result of a reset + */ void apply(View parent, View view, boolean apply, boolean reset); } @@ -403,4 +423,54 @@ public class NotificationHeaderUtil { return super.compare(parent, child, parentData, childData); } } + + private static class LeftIconApplicator implements ResultApplicator { + + public static final int[] MARGIN_ADJUSTED_VIEWS = { + R.id.notification_headerless_view_column, + R.id.line1, + R.id.notification_main_column, + R.id.notification_header}; + + @Override + public void apply(View parent, View child, boolean apply, boolean reset) { + ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon); + ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon); + if (rightIcon == null || leftIcon == null) { + return; + } + Drawable iconDrawable = rightIcon.getDrawable(); + if (iconDrawable == null) { + return; + } + rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE); + leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE); + leftIcon.setImageDrawable(apply ? iconDrawable : null); + + for (int viewId : MARGIN_ADJUSTED_VIEWS) { + adjustMargins(!apply, child.findViewById(viewId)); + } + } + + void adjustMargins(boolean iconVisible, View target) { + if (target == null) { + return; + } + Integer value = (Integer) target.getTag(iconVisible + ? com.android.internal.R.id.tag_margin_end_when_icon_visible + : com.android.internal.R.id.tag_margin_end_when_icon_gone); + if (value == null) { + return; + } + if (target instanceof NotificationHeaderView) { + ((NotificationHeaderView) target).setTopLineExtraMarginEnd(value); + } else { + ViewGroup.LayoutParams layoutParams = target.getLayoutParams(); + if (layoutParams instanceof ViewGroup.MarginLayoutParams) { + ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value); + target.setLayoutParams(layoutParams); + } + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java index 967524ce308d..ecd0c41ff82d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AnimatableProperty.java @@ -40,6 +40,14 @@ public abstract class AnimatableProperty { View.TRANSLATION_X, R.id.x_animator_tag, R.id.x_animator_tag_start_value, R.id.x_animator_tag_end_value); + public static final AnimatableProperty SCALE_X = AnimatableProperty.from( + View.SCALE_X, R.id.scale_x_animator_tag, R.id.scale_x_animator_start_value_tag, + R.id.scale_x_animator_end_value_tag); + + public static final AnimatableProperty SCALE_Y = AnimatableProperty.from( + View.SCALE_Y, R.id.scale_y_animator_tag, R.id.scale_y_animator_start_value_tag, + R.id.scale_y_animator_end_value_tag); + /** * Similar to X, however this doesn't allow for any other modifications other than from this * property. When using X, it's possible that the view is laid out during the animation, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 44b9bd26aa38..d1ab7ea55d57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -19,18 +19,25 @@ package com.android.systemui.statusbar.notification import android.app.Notification import android.content.Context import android.content.pm.LauncherApps +import android.graphics.drawable.AnimatedImageDrawable import android.os.Handler import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import com.android.internal.statusbar.NotificationVisibility import com.android.internal.widget.ConversationLayout +import com.android.internal.widget.MessagingImageMessage +import com.android.internal.widget.MessagingLayout import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationContentView import com.android.systemui.statusbar.notification.stack.StackStateAnimator +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener +import com.android.systemui.util.children import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject @@ -58,6 +65,71 @@ class ConversationNotificationProcessor @Inject constructor( } /** + * Tracks state related to animated images inside of notifications. Ex: starting and stopping + * animations to conserve CPU and memory. + */ +@SysUISingleton +class AnimatedImageNotificationManager @Inject constructor( + private val notificationEntryManager: NotificationEntryManager, + private val headsUpManager: HeadsUpManager, + private val statusBarStateController: StatusBarStateController +) { + + private var isStatusBarExpanded = false + + /** Begins listening to state changes and updating animations accordingly. */ + fun bind() { + headsUpManager.addListener(object : OnHeadsUpChangedListener { + override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { + entry.row?.let { row -> + updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded) + } + } + }) + statusBarStateController.addCallback(object : StatusBarStateController.StateListener { + override fun onExpandedChanged(isExpanded: Boolean) { + isStatusBarExpanded = isExpanded + notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry -> + entry.row?.let { row -> + updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp) + } + } + } + }) + notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener { + override fun onEntryInflated(entry: NotificationEntry) { + entry.row?.let { row -> + updateAnimatedImageDrawables( + row, + animating = isStatusBarExpanded || row.isHeadsUp) + } + } + override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry) + }) + } + + private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) = + (row.layouts?.asSequence() ?: emptySequence()) + .flatMap { layout -> layout.allViews.asSequence() } + .flatMap { view -> + (view as? ConversationLayout)?.messagingGroups?.asSequence() + ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() + ?: emptySequence() + } + .flatMap { messagingGroup -> messagingGroup.messageContainer.children } + .mapNotNull { view -> + (view as? MessagingImageMessage) + ?.let { imageMessage -> + imageMessage.drawable as? AnimatedImageDrawable + } + } + .forEach { animatedImageDrawable -> + if (animating) animatedImageDrawable.start() + else animatedImageDrawable.stop() + } +} + +/** * Tracks state related to conversation notifications, and updates the UI of existing notifications * when necessary. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 363a08566fd3..1f9bc77b0b2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -364,18 +364,13 @@ public class InstantAppNotifier extends SystemUI @Nullable private Intent getTaskIntent(int taskId, int userId) { - try { - final List<ActivityManager.RecentTaskInfo> tasks = - ActivityTaskManager.getService() - .getRecentTasks(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId) - .getList(); - for (int i = 0; i < tasks.size(); i++) { - if (tasks.get(i).id == taskId) { - return tasks.get(i).baseIntent; - } + final List<ActivityManager.RecentTaskInfo> tasks = + ActivityTaskManager.getInstance().getRecentTasks( + NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId); + for (int i = 0; i < tasks.size(); i++) { + if (tasks.get(i).id == taskId) { + return tasks.get(i).baseIntent; } - } catch (RemoteException e) { - // Fall through } return null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java index db5458664023..2586e9403e01 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MediaNotificationProcessor.java @@ -29,7 +29,7 @@ import androidx.annotation.VisibleForTesting; import androidx.palette.graphics.Palette; import com.android.internal.util.ContrastColorUtil; -import com.android.systemui.R; +import com.android.settingslib.Utils; import java.util.List; @@ -143,7 +143,8 @@ public class MediaNotificationProcessor { int foregroundColor = selectForegroundColor(backgroundColor, palette); builder.setColorPalette(backgroundColor, foregroundColor); } else { - backgroundColor = mContext.getColor(R.color.notification_material_background_color); + backgroundColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground) + .getDefaultColor(); } Bitmap colorized = mColorizer.colorize(drawable, backgroundColor, mContext.getResources().getConfiguration().getLayoutDirection() == diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 8f352ad55041..54ce4ede9770 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -22,6 +22,7 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.FeatureFlags import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter +import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager @@ -71,7 +72,8 @@ class NotificationsControllerImpl @Inject constructor( private val headsUpManager: HeadsUpManager, private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, - private val clickerBuilder: NotificationClicker.Builder + private val clickerBuilder: NotificationClicker.Builder, + private val animatedImageNotificationManager: AnimatedImageNotificationManager ) : NotificationsController { override fun initialize( @@ -100,6 +102,7 @@ class NotificationsControllerImpl @Inject constructor( bindRowCallback) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() + animatedImageNotificationManager.bind() if (featureFlags.isNewNotifPipelineEnabled) { newNotifPipeline.get().initialize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 10273cbbebad..86ebc6b2c4ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -34,6 +34,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.internal.jank.InteractionJankMonitor; +import com.android.settingslib.Utils; import com.android.systemui.Gefingerpoken; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -169,13 +170,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void updateColors() { - mNormalColor = mContext.getColor(R.color.notification_material_background_color); + mNormalColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground) + .getDefaultColor(); mTintedRippleColor = mContext.getColor( R.color.notification_ripple_tinted_color); mNormalRippleColor = mContext.getColor( R.color.notification_ripple_untinted_color); mDimmedAlpha = Color.alpha(mContext.getColor( - R.color.notification_material_background_dimmed_color)); + R.color.notification_background_dimmed_color)); } private void initDimens() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5ed17f1ee07f..10118e4733ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -558,7 +558,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setChronometerRunning(true); } if (mNotificationParent != null) { - mNotificationParent.updateChildrenHeaderAppearance(); + mNotificationParent.updateChildrenAppearance(); } onAttachedChildrenCountChanged(); // The public layouts expand button is always visible @@ -2337,7 +2337,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } getShowingLayout().updateBackgroundColor(false /* animate */); mPrivateLayout.updateExpandButtons(isExpandable()); - updateChildrenHeaderAppearance(); + updateChildrenAppearance(); updateChildrenVisibility(); applyChildrenRoundness(); } @@ -2382,9 +2382,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return channels; } - public void updateChildrenHeaderAppearance() { + /** + * If this is a group, update the appearance of the children. + */ + public void updateChildrenAppearance() { if (mIsSummaryWithChildren) { - mChildrenContainer.updateChildrenHeaderAppearance(); + mChildrenContainer.updateChildrenAppearance(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java index 7071b73c2ebf..8ac5b933e4f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; -import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -35,7 +34,6 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -168,9 +166,6 @@ public class NotificationGuts extends FrameLayout { } } }; - final TypedArray ta = context.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.Theme, 0, 0); - ta.recycle(); } public NotificationGuts(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index 7bd192d850c1..44ccb68cce4a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -19,10 +19,7 @@ package com.android.systemui.statusbar.notification.row; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; @@ -81,7 +78,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @return True if has its internal cache, false otherwise. */ public boolean hasCache() { - return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); + return mImageCache != null && !isLowRam(); } private boolean isLowRam() { @@ -110,11 +107,6 @@ public class NotificationInlineImageResolver implements ImageResolver { : R.dimen.notification_custom_view_max_image_height); } - @VisibleForTesting - protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException { - return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext); - } - /** * To resolve image from specified uri directly. If the resulting image is larger than the * maximum allowed size, scale it down. @@ -123,13 +115,7 @@ public class NotificationInlineImageResolver implements ImageResolver { * @throws IOException Throws if failed at resolving the image. */ Drawable resolveImage(Uri uri) throws IOException { - BitmapDrawable image = resolveImageInternal(uri); - if (image == null || image.getBitmap() == null) { - throw new IOException("resolveImageInternal returned null for uri: " + uri); - } - Bitmap bitmap = image.getBitmap(); - image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight)); - return image; + return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 05db67d706cf..37d5da24a704 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -64,6 +64,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { private ImageView mWorkProfileImage; private View mAudiblyAlertedIcon; private View mFeedbackIcon; + private View mLeftIcon; private View mRightIcon; private boolean mIsLowPriority; @@ -108,6 +109,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text); mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button); mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target); + mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon); mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon); mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge); mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header); @@ -146,6 +148,9 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { updateCropToPaddingForImageViews(); Notification notification = row.getEntry().getSbn().getNotification(); mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); + if (mLeftIcon != null) { + mLeftIcon.setClipToOutline(true); + } if (mRightIcon != null) { mRightIcon.setClipToOutline(true); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 6920e3f4a7c6..416c5af93400 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -38,6 +38,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; import com.android.internal.widget.CachingIconView; +import com.android.settingslib.Utils; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -324,8 +325,8 @@ public abstract class NotificationViewWrapper implements TransformableView { if (customBackgroundColor != 0) { return customBackgroundColor; } - return mView.getContext().getColor( - com.android.internal.R.color.notification_material_background_color); + return Utils.getColorAttr(mView.getContext(), android.R.attr.colorBackground) + .getDefaultColor(); } public void setLegacy(boolean legacy) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index b04f94ce9c1d..601fc197cfa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -34,7 +34,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.CachingIconView; import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; -import com.android.systemui.statusbar.NotificationHeaderUtil; +import com.android.systemui.statusbar.NotificationGroupingUtil; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -94,7 +94,7 @@ public class NotificationChildrenContainer extends ViewGroup { private NotificationViewWrapper mNotificationHeaderWrapper; private NotificationHeaderView mNotificationHeaderLowPriority; private NotificationViewWrapper mNotificationHeaderWrapperLowPriority; - private NotificationHeaderUtil mHeaderUtil; + private NotificationGroupingUtil mGroupingUtil; private ViewState mHeaderViewState; private int mClipBottomAmount; private boolean mIsLowPriority; @@ -299,7 +299,7 @@ public class NotificationChildrenContainer extends ViewGroup { row.setSystemChildExpanded(false); row.setUserLocked(false); if (!row.isRemoved()) { - mHeaderUtil.restoreNotificationHeader(row); + mGroupingUtil.restoreChildNotification(row); } } @@ -341,7 +341,7 @@ public class NotificationChildrenContainer extends ViewGroup { } recreateLowPriorityHeader(builder, isConversation); updateHeaderVisibility(false /* animate */); - updateChildrenHeaderAppearance(); + updateChildrenAppearance(); } /** @@ -389,8 +389,11 @@ public class NotificationChildrenContainer extends ViewGroup { } } - public void updateChildrenHeaderAppearance() { - mHeaderUtil.updateChildrenHeaderAppearance(); + /** + * Update the appearance of the children to reduce redundancies. + */ + public void updateChildrenAppearance() { + mGroupingUtil.updateChildrenAppearance(); } public void updateGroupOverflow() { @@ -861,7 +864,7 @@ public class NotificationChildrenContainer extends ViewGroup { public void setContainingNotification(ExpandableNotificationRow parent) { mContainingNotification = parent; - mHeaderUtil = new NotificationHeaderUtil(mContainingNotification); + mGroupingUtil = new NotificationGroupingUtil(mContainingNotification); } public ExpandableNotificationRow getContainingNotification() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ba5f95e9c4d8..5cc17a08504f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -50,7 +50,6 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.MathUtils; import android.util.Pair; -import android.view.ContextThemeWrapper; import android.view.DisplayCutout; import android.view.InputDevice; import android.view.LayoutInflater; @@ -385,7 +384,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private float mBackgroundXFactor = 1f; - private boolean mUsingLightTheme; private boolean mQsExpanded; private boolean mForwardScrollable; private boolean mBackwardScrollable; @@ -4122,15 +4120,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param lightTheme True if light theme should be used. */ @ShadeViewRefactor(RefactorComponent.DECORATOR) - void updateDecorViews(boolean lightTheme) { - if (lightTheme == mUsingLightTheme) { - return; - } - mUsingLightTheme = lightTheme; - Context context = new ContextThemeWrapper(mContext, - lightTheme ? R.style.Theme_SystemUI_Light : R.style.Theme_SystemUI); + void updateDecorViews() { final @ColorInt int textColor = - Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor); + Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); mSectionsManager.setHeaderForegroundColor(textColor); mFooterView.setTextColor(textColor); mEmptyShadeView.setTextColor(textColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index abbbbb8352f5..8a0330912502 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -162,7 +162,6 @@ public class NotificationStackScrollLayoutController { private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; - private final SysuiColorExtractor mColorExtractor; private final NotificationLockscreenUserManager mLockscreenUserManager; // TODO: StatusBar should be encapsulated behind a Controller private final StatusBar mStatusBar; @@ -224,12 +223,14 @@ public class NotificationStackScrollLayoutController { public void onOverlayChanged() { updateShowEmptyShadeView(); mView.updateCornerRadius(); + mView.updateBgColor(); mView.reinflateViews(); } @Override public void onUiModeChanged() { mView.updateBgColor(); + mView.updateDecorViews(); } @Override @@ -577,7 +578,6 @@ public class NotificationStackScrollLayoutController { mKeyguardMediaController = keyguardMediaController; mKeyguardBypassController = keyguardBypassController; mZenModeController = zenModeController; - mColorExtractor = colorExtractor; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; mFalsingCollector = falsingCollector; @@ -689,12 +689,6 @@ public class NotificationStackScrollLayoutController { Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); - mOnColorsChangedListener = (colorExtractor, which) -> { - final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); - mView.updateDecorViews(useDarkText); - }; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mKeyguardMediaController.setVisibilityChangedListener(visible -> { mView.setKeyguardMediaControllorVisible(visible); if (visible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 7508dcb487ed..1a2d1cfdcfb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; +import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale; import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; import android.content.res.Resources; @@ -184,6 +185,7 @@ public class KeyguardClockPositionAlgorithm { result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding : getClockY(1.0f) + mKeyguardStatusHeight; result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); + result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount); } /** @@ -304,6 +306,11 @@ public class KeyguardClockPositionAlgorithm { public float clockAlpha; /** + * Amount to scale the large clock (0.0 - 1.0) + */ + public float clockScale; + + /** * The top padding of the stack scroller, in pixels. */ public int stackScrollerPadding; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 54fb863b5de7..0df3347b8f89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -49,7 +49,7 @@ public class LockIcon extends KeyguardAffordanceView { static final int STATE_SCANNING_FACE = 2; static final int STATE_BIOMETRICS_ERROR = 3; private float mDozeAmount; - private int mIconColor; + private int mIconColor = Color.TRANSPARENT; private int mOldState; private int mState; private boolean mDozing; @@ -149,7 +149,10 @@ public class LockIcon extends KeyguardAffordanceView { updateDarkTint(); } - void onThemeChange(int iconColor) { + void updateColor(int iconColor) { + if (mIconColor == iconColor) { + return; + } mDrawableCache.clear(); mIconColor = iconColor; updateDarkTint(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java index 289ff71dcb46..0e7e2fd8173c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java @@ -84,6 +84,7 @@ public class LockscreenLockIconController { private boolean mDocked; private boolean mWakeAndUnlockRunning; private boolean mShowingLaunchAffordance; + private boolean mBouncerShowing; private boolean mBouncerShowingScrimmed; private boolean mFingerprintUnlock; private int mStatusBarState = StatusBarState.SHADE; @@ -142,16 +143,13 @@ public class LockscreenLockIconController { private int mDensity; @Override - public void onThemeChanged() { - if (mLockIcon == null) { - return; - } + public void onUiModeChanged() { + updateColor(); + } - TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( - null, new int[]{ R.attr.wallpaperTextColor }, 0, 0); - int iconColor = typedArray.getColor(0, Color.WHITE); - typedArray.recycle(); - mLockIcon.onThemeChange(iconColor); + @Override + public void onOverlayChanged() { + updateColor(); } @Override @@ -350,6 +348,7 @@ public class LockscreenLockIconController { */ public void attach(LockIcon lockIcon) { mLockIcon = lockIcon; + updateColor(); mLockIcon.setOnClickListener(this::handleClick); mLockIcon.setOnLongClickListener(this::handleLongClick); @@ -408,11 +407,22 @@ public class LockscreenLockIconController { } /** Sets whether the bouncer is showing. */ - public void setBouncerShowingScrimmed(boolean bouncerShowing) { - mBouncerShowingScrimmed = bouncerShowing; - if (mKeyguardBypassController.getBypassEnabled()) { - update(); + public void setBouncerShowingScrimmed(boolean showing, boolean scrimmed) { + mBouncerShowing = showing; + mBouncerShowingScrimmed = scrimmed; + update(); + } + + private void updateColor() { + if (mLockIcon == null) { + return; } + + TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes( + null, new int[]{ android.R.attr.textColorPrimary }, 0, 0); + int iconColor = typedArray.getColor(0, Color.WHITE); + typedArray.recycle(); + mLockIcon.updateColor(iconColor); } /** @@ -510,7 +520,10 @@ public class LockscreenLockIconController { return changed; } boolean onAodOrDocked = mStatusBarStateController.isDozing() || mDocked; - boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; + boolean onKeyguardWithoutBouncer = mStatusBarState == StatusBarState.KEYGUARD + && !mBouncerShowing; + boolean invisible = onAodOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance + || onKeyguardWithoutBouncer; boolean fingerprintOrBypass = mFingerprintUnlock || mKeyguardBypassController.getBypassEnabled(); if (fingerprintOrBypass && !mBouncerShowingScrimmed) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f7139aa2acce..9c70d52d4076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -68,6 +68,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; +import com.android.keyguard.DisabledUdfpsController; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; import com.android.keyguard.KeyguardStatusViewController; @@ -85,6 +86,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; @@ -139,6 +141,7 @@ import java.util.function.Consumer; import java.util.function.Function; import javax.inject.Inject; +import javax.inject.Provider; @StatusBarComponent.StatusBarScope public class NotificationPanelViewController extends PanelViewController { @@ -183,7 +186,7 @@ public class NotificationPanelViewController extends PanelViewController { private final MetricsLogger mMetricsLogger; private final ActivityManager mActivityManager; private final ConfigurationController mConfigurationController; - private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder; + private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder; private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private final NotificationIconAreaController mNotificationIconAreaController; @@ -256,7 +259,25 @@ public class NotificationPanelViewController extends PanelViewController { mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mDelayShowingKeyguardStatusBar = false; } - }; + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + if (mDisabledUdfpsController == null + && mAuthController.getUdfpsRegion() != null + && mAuthController.isUdfpsEnrolled( + KeyguardUpdateMonitor.getCurrentUser())) { + LayoutInflater.from(mView.getContext()) + .inflate(R.layout.disabled_udfps_view, mView); + mDisabledUdfpsController = new DisabledUdfpsController( + mView.findViewById(R.id.disabled_udfps_view), + mStatusBarStateController, + mUpdateMonitor, + mAuthController, + mStatusBarKeyguardViewManager); + mDisabledUdfpsController.init(); + } + } + }; private final InjectionInflationController mInjectionInflationController; private final PowerManager mPowerManager; @@ -284,7 +305,7 @@ public class NotificationPanelViewController extends PanelViewController { private QS mQs; private FrameLayout mQsFrame; private KeyguardStatusViewController mKeyguardStatusViewController; - private View mQsNavbarScrim; + private DisabledUdfpsController mDisabledUdfpsController; private NotificationsQuickSettingsContainer mNotificationContainerParent; private boolean mAnimateNextPositionUpdate; @@ -435,6 +456,7 @@ public class NotificationPanelViewController extends PanelViewController { private final CommandQueue mCommandQueue; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; + private final MediaDataManager mMediaDataManager; private int mDisplayId; /** @@ -515,7 +537,7 @@ public class NotificationPanelViewController extends PanelViewController { KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, - FlingAnimationUtils.Builder flingAnimationUtilsBuilder, + Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, @@ -526,10 +548,11 @@ public class NotificationPanelViewController extends PanelViewController { NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, - QSDetailDisplayer qsDetailDisplayer) { + QSDetailDisplayer qsDetailDisplayer, + MediaDataManager mediaDataManager) { super(view, falsingManager, dozeLog, keyguardStateController, (SysuiStatusBarStateController) statusBarStateController, vibratorHelper, - latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager); + latencyTracker, flingAnimationUtilsBuilder.get(), statusBarTouchableRegionManager); mView = view; mMetricsLogger = metricsLogger; mActivityManager = activityManager; @@ -556,6 +579,7 @@ public class NotificationPanelViewController extends PanelViewController { mPulseExpansionHandler = pulseExpansionHandler; mDozeParameters = dozeParameters; mBiometricUnlockController = biometricUnlockController; + mMediaDataManager = mediaDataManager; pulseExpansionHandler.setPulseExpandAbortListener(() -> { if (mQs != null) { mQs.animateHeaderSlidingOut(); @@ -630,7 +654,6 @@ public class NotificationPanelViewController extends PanelViewController { mOnEmptySpaceClickListener); addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp); mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area); - mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim); mLastOrientation = mResources.getConfiguration().orientation; initBottomArea(); @@ -668,7 +691,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void loadDimens() { super.loadDimens(); - mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset() + mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get() .setMaxLengthSeconds(0.4f).build(); mStatusBarMinHeight = mResources.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); @@ -872,7 +895,8 @@ public class NotificationPanelViewController extends PanelViewController { int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = !bypassEnabled - && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0; + && (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0 + || mMediaDataManager.hasActiveMedia()); mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), @@ -888,7 +912,8 @@ public class NotificationPanelViewController extends PanelViewController { mUpdateMonitor.isUdfpsEnrolled()); mClockPositionAlgorithm.run(mClockPositionResult); mKeyguardStatusViewController.updatePosition( - mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock); + mClockPositionResult.clockX, mClockPositionResult.clockY, + mClockPositionResult.clockScale, animateClock); updateNotificationTranslucency(); updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; @@ -1709,9 +1734,6 @@ public class NotificationPanelViewController extends PanelViewController { mBarState != KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); - mQsNavbarScrim.setVisibility( - mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling - && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE); if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */); } @@ -1736,10 +1758,6 @@ public class NotificationPanelViewController extends PanelViewController { updateKeyguardBottomAreaAlpha(); updateBigClockAlpha(); } - if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling - && mQsScrimEnabled) { - mQsNavbarScrim.setAlpha(getQsExpansionFraction()); - } if (mAccessibilityManager.isEnabled()) { mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle()); @@ -3044,6 +3062,9 @@ public class NotificationPanelViewController extends PanelViewController { if (mKeyguardStatusBar != null) { mKeyguardStatusBar.dump(fd, pw, args); } + if (mDisabledUdfpsController != null) { + mDisabledUdfpsController.dump(fd, pw, args); + } } public boolean hasActiveClearableNotifications() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 4af27877c201..9e7efc12f4f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -37,22 +37,21 @@ import android.view.animation.Interpolator; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.function.TriConsumer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.settingslib.Utils; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.AlarmTimeout; import com.android.systemui.util.wakelock.DelayedWakeLock; @@ -71,8 +70,7 @@ import javax.inject.Inject; * security method gets shown). */ @SysUISingleton -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnColorsChangedListener, - Dumpable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -125,12 +123,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * The default scrim under the shade and dialogs. * This should not be lower than 0.54, otherwise we won't pass GAR. */ - public static final float BUSY_SCRIM_ALPHA = 0.85f; + public static final float BUSY_SCRIM_ALPHA = 1f; /** * Same as above, but when blur is supported. */ - public static final float BLUR_SCRIM_ALPHA = 0.54f; + public static final float BLUR_SCRIM_ALPHA = 0.80f; static final int TAG_KEY_ANIM = R.id.scrim; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; @@ -153,8 +151,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo private final AlarmTimeout mTimeTicker; private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; private final Handler mHandler; + private final BlurUtils mBlurUtils; - private final SysuiColorExtractor mColorExtractor; private GradientColors mColors; private boolean mNeedsDrawableColorUpdate; @@ -204,12 +202,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, - KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor, - DockManager dockManager, BlurUtils blurUtils) { + KeyguardUpdateMonitor keyguardUpdateMonitor, DockManager dockManager, + BlurUtils blurUtils, ConfigurationController configurationController) { mScrimStateListener = lightBarController::setScrimState; mDefaultScrimAlpha = blurUtils.supportsBlursOnWindows() ? BLUR_SCRIM_ALPHA : BUSY_SCRIM_ALPHA; + mBlurUtils = blurUtils; mKeyguardStateController = keyguardStateController; mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); @@ -230,11 +229,24 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo keyguardStateController.getKeyguardFadingAwayDuration()); } }); + configurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onThemeChanged() { + ScrimController.this.onThemeChanged(); + } - mColorExtractor = sysuiColorExtractor; - mColorExtractor.addOnColorsChangedListener(this); - mColors = mColorExtractor.getNeutralColors(); - mNeedsDrawableColorUpdate = true; + @Override + public void onOverlayChanged() { + ScrimController.this.onThemeChanged(); + } + + @Override + public void onUiModeChanged() { + ScrimController.this.onThemeChanged(); + } + }); + + mColors = new GradientColors(); } /** @@ -245,6 +257,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimBehind = scrimBehind; mScrimInFront = scrimInFront; mScrimForBubble = scrimForBubble; + updateThemeColors(); if (mScrimBehindChangeRunnable != null) { mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable); @@ -619,11 +632,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo mScrimInFront.setColors(mColors, animateScrimInFront); mScrimBehind.setColors(mColors, animateScrimBehind); - // Calculate minimum scrim opacity for white or black text. - int textColor = mColors.supportsDarkText() ? Color.BLACK : Color.WHITE; - int mainColor = mColors.getMainColor(); - float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor, - 4.5f /* minimumContrast */) / 255f; dispatchScrimState(mScrimBehind.getViewAlpha()); } @@ -968,10 +976,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo // Don't care in the base class. } - @Override - public void onColorsChanged(ColorExtractor colorExtractor, int which) { - mColors = mColorExtractor.getNeutralColors(); + private void updateThemeColors() { + int background = Utils.getColorAttr(mScrimBehind.getContext(), + android.R.attr.colorBackgroundFloating).getDefaultColor(); + int accent = Utils.getColorAccent(mScrimBehind.getContext()).getDefaultColor(); + mColors.setMainColor(background); + mColors.setSecondaryColor(accent); + mColors.setSupportsDarkText( + ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); mNeedsDrawableColorUpdate = true; + } + + private void onThemeChanged() { + updateThemeColors(); scheduleUpdate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index c8c5a6331a3b..9e872ab65591 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -3782,7 +3782,8 @@ public class StatusBar extends SystemUI implements DemoMode, mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); mPulseExpansionHandler.setBouncerShowing(bouncerShowing); - mLockscreenLockIconController.setBouncerShowingScrimmed(isBouncerShowingScrimmed()); + mLockscreenLockIconController.setBouncerShowingScrimmed(bouncerShowing, + isBouncerShowingScrimmed()); if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing); updateHideIconsForBouncer(true /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 79f09158fc67..adbc85b8e2e5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -42,6 +42,7 @@ import android.text.SpannedString; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; +import android.view.ContentInfo; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -590,12 +591,12 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene new OnReceiveContentListener() { @Override @Nullable - public Payload onReceiveContent(@NonNull View view, - @NonNull Payload payload) { - Map<Boolean, Payload> split = payload.partition( + public ContentInfo onReceiveContent(@NonNull View view, + @NonNull ContentInfo payload) { + Map<Boolean, ContentInfo> split = payload.partition( item -> item.getUri() != null); - Payload uriItems = split.get(true); - Payload remainingItems = split.get(false); + ContentInfo uriItems = split.get(true); + ContentInfo remainingItems = split.get(false); if (uriItems != null) { ClipData clip = uriItems.getClip(); ClipDescription description = clip.getDescription(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2795857383e8..bdf2b0c24ba5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -71,7 +71,6 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks { // Creating AudioRecordingDisclosureBar and just letting it run new AudioRecordingDisclosureBar(mContext); } - } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java new file mode 100644 index 000000000000..3b1a4db067a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.app.Notification; +import android.app.PendingIntent; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.systemui.R; + +/** + * Adapter for the VerticalGridView of the TvNotificationsPanelView. + */ +public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + private static final String TAG = "TvNotificationAdapter"; + private SparseArray<StatusBarNotification> mNotifications; + + public TvNotificationAdapter() { + setHasStableIds(true); + } + + @NonNull + @Override + public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item, + parent, false); + return new TvNotificationViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) { + if (mNotifications == null) { + Log.e(TAG, "Could not bind view holder because the notification is missing"); + return; + } + + TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder; + Notification notification = mNotifications.valueAt(position).getNotification(); + holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE)); + holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT)); + holder.mPendingIntent = notification.contentIntent; + } + + @Override + public int getItemCount() { + return mNotifications == null ? 0 : mNotifications.size(); + } + + @Override + public long getItemId(int position) { + // the item id is the notification id + return mNotifications.keyAt(position); + } + + /** + * Updates the notifications and calls notifyDataSetChanged(). + */ + public void setNotifications(SparseArray<StatusBarNotification> notifications) { + this.mNotifications = notifications; + notifyDataSetChanged(); + } + + private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements + View.OnClickListener { + final TextView mTitle; + final TextView mDetails; + PendingIntent mPendingIntent; + + protected TvNotificationViewHolder(View itemView) { + super(itemView); + mTitle = itemView.findViewById(R.id.tv_notification_title); + mDetails = itemView.findViewById(R.id.tv_notification_details); + itemView.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + try { + if (mPendingIntent != null) { + mPendingIntent.send(); + } + } catch (PendingIntent.CanceledException e) { + Log.d(TAG, "Pending intent canceled for : " + mPendingIntent); + } + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java new file mode 100644 index 000000000000..d985803c2b39 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.annotation.Nullable; +import android.app.Notification; +import android.content.Context; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; +import android.util.Log; +import android.util.SparseArray; + +import com.android.systemui.SystemUI; +import com.android.systemui.statusbar.NotificationListener; + +import javax.inject.Inject; + +/** + * Keeps track of the notifications on TV. + */ +public class TvNotificationHandler extends SystemUI implements + NotificationListener.NotificationHandler { + private static final String TAG = "TvNotificationHandler"; + private final NotificationListener mNotificationListener; + private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>(); + @Nullable + private Listener mUpdateListener; + + @Inject + public TvNotificationHandler(Context context, NotificationListener notificationListener) { + super(context); + mNotificationListener = notificationListener; + } + + public SparseArray<StatusBarNotification> getCurrentNotifications() { + return mNotifications; + } + + public void setTvNotificationListener(Listener listener) { + mUpdateListener = listener; + } + + @Override + public void start() { + mNotificationListener.addNotificationHandler(this); + mNotificationListener.registerAsSystemService(); + } + + @Override + public void onNotificationPosted(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap) { + if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) { + Log.v(TAG, "Notification not added because it isn't relevant for tv"); + return; + } + + mNotifications.put(sbn.getId(), sbn); + if (mUpdateListener != null) { + mUpdateListener.notificationsUpdated(mNotifications); + } + Log.d(TAG, "Notification added"); + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap) { + + if (mNotifications.contains(sbn.getId())) { + mNotifications.remove(sbn.getId()); + Log.d(TAG, "Notification removed"); + + if (mUpdateListener != null) { + mUpdateListener.notificationsUpdated(mNotifications); + } + } + } + + @Override + public void onNotificationRemoved(StatusBarNotification sbn, + NotificationListenerService.RankingMap rankingMap, int reason) { + onNotificationRemoved(sbn, rankingMap); + } + + @Override + public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) { + // noop + } + + @Override + public void onNotificationsInitialized() { + // noop + } + + /** + * Get notified when the notifications are updated. + */ + interface Listener { + void notificationsUpdated(SparseArray<StatusBarNotification> sbns); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java index 0bd36240a366..477424c55a73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.tv; +package com.android.systemui.statusbar.tv.notifications; import android.Manifest; import android.app.NotificationManager; @@ -59,9 +59,8 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba startNotificationHandlerActivity( new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL)); } else { - Log.w(TAG, - "Not toggling notification panel: config_notificationHandlerPackage is " - + "empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL); } } @@ -71,9 +70,8 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba startNotificationHandlerActivity( new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL)); } else { - Log.w(TAG, - "Not expanding notification panel: config_notificationHandlerPackage is " - + "empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL); } } @@ -86,11 +84,17 @@ public class TvNotificationPanel extends SystemUI implements CommandQueue.Callba closeNotificationIntent.setPackage(mNotificationHandlerPackage); mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT); } else { - Log.w(TAG, - "Not closing notification panel: config_notificationHandlerPackage is empty"); + openInternalNotificationPanel( + NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL); } } + private void openInternalNotificationPanel(String action) { + Intent intent = new Intent(mContext, TvNotificationPanelActivity.class); + intent.setAction(action); + mContext.startActivityAsUser(intent, UserHandle.SYSTEM); + } + /** * Starts the activity intent if all of the following are true * <ul> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java new file mode 100644 index 000000000000..30f401b91d25 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tv.notifications; + +import android.annotation.NonNull; +import android.app.Activity; +import android.app.NotificationManager; +import android.content.Intent; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.util.SparseArray; +import android.view.View; + +import androidx.leanback.widget.VerticalGridView; + +import com.android.systemui.R; + +import javax.inject.Inject; + +/** + * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can + * be found to show the notifications. + */ +public class TvNotificationPanelActivity extends Activity implements + TvNotificationHandler.Listener { + private final TvNotificationHandler mTvNotificationHandler; + private TvNotificationAdapter mTvNotificationAdapter; + private VerticalGridView mNotificationListView; + private View mNotificationPlaceholder; + private boolean mPanelAlreadyOpen = false; + + @Inject + public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) { + super(); + mTvNotificationHandler = tvNotificationHandler; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (maybeClosePanel(getIntent())) { + return; + } + mPanelAlreadyOpen = true; + + setContentView(R.layout.tv_notification_panel); + + mNotificationPlaceholder = findViewById(R.id.no_tv_notifications); + mTvNotificationAdapter = new TvNotificationAdapter(); + + mNotificationListView = findViewById(R.id.notifications_list); + mNotificationListView.setAdapter(mTvNotificationAdapter); + mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width); + + mTvNotificationHandler.setTvNotificationListener(this); + notificationsUpdated(mTvNotificationHandler.getCurrentNotifications()); + } + + @Override + public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) { + mTvNotificationAdapter.setNotifications(notificationList); + + boolean noNotifications = notificationList.size() == 0; + mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE); + mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE); + } + + @Override + public void onNewIntent(Intent intent) { + super.onNewIntent(intent); + maybeClosePanel(intent); + } + + /** + * Handles intents from onCreate and onNewIntent. + * + * @return true if the panel is being closed, false if it is being opened + */ + private boolean maybeClosePanel(Intent intent) { + if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction()) + || (mPanelAlreadyOpen + && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals( + intent.getAction()))) { + finish(); + return true; + } + return false; + } + + @Override + public void onDestroy() { + super.onDestroy(); + mTvNotificationHandler.setTvNotificationListener(null); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java index 0aa2a739eb82..50cef781d9d0 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java @@ -47,7 +47,7 @@ import java.util.stream.Collectors; */ @SysUISingleton public class ThemeOverlayApplier implements Dumpable { - private static final String TAG = "ThemeOverlayManager"; + private static final String TAG = "ThemeOverlayApplier"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index c4e2b5d47ba8..006ecb937922 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -307,13 +307,13 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { if (!hasSystemPalette && mSystemOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_SYSTEM_PALETTE, ThemeOverlayApplier.MONET_SYSTEM_PALETTE_PACKAGE - + Integer.toHexString(mSystemOverlayColor).toUpperCase()); + + getColorString(mSystemOverlayColor)); } // Same for the accent color if (!hasAccentColor && mAccentOverlayColor != Color.TRANSPARENT) { categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ThemeOverlayApplier.MONET_ACCENT_COLOR_PACKAGE - + Integer.toHexString(mAccentOverlayColor).toUpperCase()); + + getColorString(mAccentOverlayColor)); } Set<UserHandle> userHandles = Sets.newHashSet(UserHandle.of(currentUser)); @@ -325,6 +325,14 @@ public class ThemeOverlayController extends SystemUI implements Dumpable { mThemeManager.applyCurrentUserOverlays(categoryToPackage, userHandles); } + private String getColorString(int color) { + String colorString = Integer.toHexString(color).toUpperCase(); + while (colorString.length() < 6) { + colorString = "0" + colorString; + } + return colorString; + } + @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println("mLockColors=" + mLockColors); diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java index 2c3ea4f452bb..353333f714d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java @@ -16,14 +16,22 @@ package com.android.systemui.tv; +import com.android.systemui.SystemUI; import com.android.systemui.dagger.GlobalRootComponent; -import com.android.systemui.wmshell.TvPipModule; +import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import dagger.Binds; import dagger.Module; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; @Module interface TvSystemUIBinder { @Binds GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent); + + @Binds + @IntoMap + @ClassKey(TvNotificationHandler.class) + SystemUI bindTvNotificationHandler(TvNotificationHandler systemui); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 8ffc7cf568ff..56a4c203e840 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -62,6 +63,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import javax.inject.Named; @@ -164,4 +166,11 @@ public abstract class TvSystemUIModule { @Binds abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost); + + @Provides + @SysUISingleton + static TvNotificationHandler provideTvNotificationHandler(Context context, + NotificationListener notificationListener) { + return new TvNotificationHandler(context, notificationListener); + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 411fbc3ad64c..a879a1ef4b77 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -53,7 +53,6 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellCommandHandler; -import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; @@ -99,13 +98,11 @@ public final class WMShell extends SystemUI private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; private final ProtoTracer mProtoTracer; private final Optional<ShellCommandHandler> mShellCommandHandler; - private final Optional<AppPairs> mAppPairsOptional; private boolean mIsSysUiStateValid; private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; - private KeyguardUpdateMonitorCallback mAppPairsKeyguardCallback; @Inject public WMShell(Context context, CommandQueue commandQueue, @@ -119,8 +116,7 @@ public final class WMShell extends SystemUI Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutoutOptional, ProtoTracer protoTracer, - Optional<ShellCommandHandler> shellCommandHandler, - Optional<AppPairs> appPairsOptional) { + Optional<ShellCommandHandler> shellCommandHandler) { super(context); mCommandQueue = commandQueue; mConfigurationController = configurationController; @@ -135,7 +131,6 @@ public final class WMShell extends SystemUI mProtoTracer = protoTracer; mProtoTracer.add(this); mShellCommandHandler = shellCommandHandler; - mAppPairsOptional = appPairsOptional; } @Override @@ -145,7 +140,6 @@ public final class WMShell extends SystemUI mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout); - mAppPairsOptional.ifPresent(this::initAppPairs); } @VisibleForTesting @@ -294,16 +288,6 @@ public final class WMShell extends SystemUI }); } - void initAppPairs(AppPairs appPairs) { - mAppPairsKeyguardCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - appPairs.onKeyguardVisibilityChanged(showing); - } - }; - mKeyguardUpdateMonitor.registerCallback(mAppPairsKeyguardCallback); - } - @Override public void writeToProto(SystemUiTraceProto proto) { if (proto.wmShell == null) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 4505b2a87c78..7a1c05890873 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -84,10 +84,8 @@ public class WMShellModule { @WMSingleton @Provides static AppPairs provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, DisplayController displayController, - TaskStackListenerImpl taskStackListener) { - return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, - taskStackListener); + SyncTransactionQueue syncQueue, DisplayController displayController) { + return new AppPairsController(shellTaskOrganizer, syncQueue, displayController); } @WMSingleton diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index eef38d316775..b03dc94fde33 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -34,6 +34,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -80,6 +81,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private KeyguardSecurityViewFlipper mSecurityViewFlipper; @Mock private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; + @Mock + private ConfigurationController mConfigurationController; private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @@ -92,8 +95,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, mKeyguardSecurityViewFlipperController) - .create(mSecurityCallback); + mKeyguardStateController, mKeyguardSecurityViewFlipperController, + mConfigurationController).create(mSecurityCallback); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt index 2e8eb0014f30..53d84dba6fb2 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt @@ -17,7 +17,6 @@ package com.android.keyguard import android.animation.ValueAnimator -import android.graphics.Paint import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout @@ -53,7 +52,7 @@ class TextAnimatorTest : SysuiTestCase() { val layout = makeLayout("Hello, World", PAINT[0]) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(Paint::class.java)) + val paint = arrayListOf(mock(TextPaint::class.java)) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { @@ -85,7 +84,7 @@ class TextAnimatorTest : SysuiTestCase() { val layout = makeLayout("Hello, World", PAINT[0]) val valueAnimator = mock(ValueAnimator::class.java) val textInterpolator = mock(TextInterpolator::class.java) - val paint = arrayListOf(mock(Paint::class.java)) + val paint = arrayListOf(mock(TextPaint::class.java)) `when`(textInterpolator.targetPaint).thenReturn(paint) val textAnimator = TextAnimator(layout, {}).apply { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt index 002ba364e9d4..1206dab7b56d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt @@ -18,11 +18,12 @@ package com.android.keyguard import android.graphics.Bitmap import android.graphics.Canvas -import android.graphics.Paint import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextPaint +import android.text.TextDirectionHeuristic +import android.text.TextDirectionHeuristics import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat @@ -31,6 +32,7 @@ import org.junit.runner.RunWith import kotlin.math.ceil private const val TEXT = "Hello, World." +private const val BIDI_TEXT = "abc\u05D0\u05D1\u05D2" private const val BMP_WIDTH = 400 private const val BMP_HEIGHT = 300 @@ -38,11 +40,11 @@ private val PAINT = TextPaint().apply { textSize = 32f } -private val START_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { +private val START_PAINT = arrayListOf(TextPaint(PAINT).apply { fontVariationSettings = "'wght' 400" }) -private val END_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { +private val END_PAINT = arrayListOf(TextPaint(PAINT).apply { fontVariationSettings = "'wght' 700" }) @@ -50,9 +52,14 @@ private val END_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply { @SmallTest class TextInterpolatorTest : SysuiTestCase() { - private fun makeLayout(text: String, paint: TextPaint): Layout { + private fun makeLayout( + text: String, + paint: TextPaint, + dir: TextDirectionHeuristic = TextDirectionHeuristics.LTR + ): Layout { val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt() - return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build() + return StaticLayout.Builder.obtain(text, 0, text.length, paint, width) + .setTextDirection(dir).build() } @Test @@ -69,7 +76,7 @@ class TextInterpolatorTest : SysuiTestCase() { // Just after created TextInterpolator, it should have 0 progress. assertThat(interp.progress).isEqualTo(0f) val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, START_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -87,7 +94,7 @@ class TextInterpolatorTest : SysuiTestCase() { interp.progress = 1f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - val expected = makeLayout(TEXT, END_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT) assertThat(expected.sameAs(actual)).isTrue() } @@ -108,9 +115,9 @@ class TextInterpolatorTest : SysuiTestCase() { // end state. interp.progress = 0.5f val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) - assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0] as TextPaint) + assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0]) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() - assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0] as TextPaint) + assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0]) .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse() } @@ -135,10 +142,50 @@ class TextInterpolatorTest : SysuiTestCase() { assertThat(expected.sameAs(actual)).isTrue() } + + @Test + fun testBidi_LTR() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR) + + val interp = TextInterpolator(layout) + TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.onBasePaintModified() + + TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + assertThat(interp.progress).isEqualTo(0f) + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + assertThat(expected.sameAs(actual)).isTrue() + } + + @Test + fun testBidi_RTL() { + val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL) + + val interp = TextInterpolator(layout) + TextInterpolator.updatePaint(interp.basePaint, START_PAINT) + interp.onBasePaintModified() + + TextInterpolator.updatePaint(interp.targetPaint, END_PAINT) + interp.onTargetPaintModified() + + // Just after created TextInterpolator, it should have 0 progress. + assertThat(interp.progress).isEqualTo(0f) + val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT) + val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL) + .toBitmap(BMP_WIDTH, BMP_HEIGHT) + + assertThat(expected.sameAs(actual)).isTrue() + } } private fun Layout.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }!! + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!! private fun TextInterpolator.toBitmap(width: Int, height: Int) = - Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }
\ No newline at end of file + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index ec0aa4ca89ed..30c4cf6da8d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.content.ComponentName; import android.content.Context; @@ -92,7 +93,7 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock - private IActivityTaskManager mActivityTaskManager; + private ActivityTaskManager mActivityTaskManager; @Mock private FingerprintManager mFingerprintManager; @Mock @@ -553,7 +554,7 @@ public class AuthControllerTest extends SysuiTestCase { TestableAuthController(Context context, CommandQueue commandQueue, StatusBarStateController statusBarStateController, - IActivityTaskManager activityTaskManager, + ActivityTaskManager activityTaskManager, FingerprintManager fingerprintManager, FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java new file mode 100644 index 000000000000..19f0a15c8936 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier; + +import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.any; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManagerFake; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class BrightLineClassifierTest extends SysuiTestCase { + private BrightLineFalsingManager mBrightLineFalsingManager; + @Mock + private FalsingDataProvider mFalsingDataProvider; + private final DockManagerFake mDockManager = new DockManagerFake(); + private final MetricsLogger mMetricsLogger = new FakeMetricsLogger(); + private final Set<FalsingClassifier> mClassifiers = new HashSet<>(); + @Mock + private SingleTapClassifier mSingleTapClassfier; + @Mock + private DoubleTapClassifier mDoubleTapClassifier; + @Mock + private FalsingClassifier mClassifierA; + @Mock + private FalsingClassifier mClassifierB; + private final List<MotionEvent> mMotionEventList = new ArrayList<>(); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mClassifiers.add(mClassifierA); + mClassifiers.add(mClassifierB); + when(mFalsingDataProvider.isDirty()).thenReturn(true); + when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList); + mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager, + mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier, false); + } + + @Test + public void testRegisterSessionListener() { + verify(mFalsingDataProvider).addSessionListener( + any(FalsingDataProvider.SessionListener.class)); + + mBrightLineFalsingManager.cleanup(); + verify(mFalsingDataProvider).removeSessionListener( + any(FalsingDataProvider.SessionListener.class)); + } + + @Test + public void testIsFalseTouch_NoClassifiers() { + mClassifiers.clear(); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_ClassffiersPass() { + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_ClassifierARejects() { + when(mClassifierA.isFalseTouch()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); + } + + @Test + public void testIsFalseTouch_ClassifierBRejects() { + when(mClassifierB.isFalseTouch()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isTrue(); + } + + @Test + public void testIsFalseTouch_FaceAuth() { + // Even when the classifiers report a false, we should allow. + when(mClassifierA.isFalseTouch()).thenReturn(true); + when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTouch_Docked() { + // Even when the classifiers report a false, we should allow. + when(mClassifierA.isFalseTouch()).thenReturn(true); + mDockManager.setIsDocked(true); + + assertThat(mBrightLineFalsingManager.isFalseTouch(0)).isFalse(); + } + + @Test + public void testIsFalseTap_BasicCheck() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(false); + + assertThat(mBrightLineFalsingManager.isFalseTap(false)).isTrue(); + + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseTap(false)).isFalse(); + } + + @Test + public void testIsFalseTap_RobustCheck_NoFaceAuth() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + mFalsingDataProvider.setJustUnlockedWithFace(false); + assertThat(mBrightLineFalsingManager.isFalseTap(true)).isTrue(); + } + + @Test + public void testIsFalseTap_RobustCheck_FaceAuth() { + when(mSingleTapClassfier.isTap(mMotionEventList)).thenReturn(true); + when(mFalsingDataProvider.isJustUnlockedWithFace()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTap(true)).isFalse(); + } + + @Test + public void testIsFalseDoubleTap() { + when(mDoubleTapClassifier.isFalseTouch()).thenReturn(false); + + assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isFalse(); + + when(mDoubleTapClassifier.isFalseTouch()).thenReturn(true); + + assertThat(mBrightLineFalsingManager.isFalseDoubleTap()).isTrue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java index 714d6581ff00..7659db8cc9ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DiagonalClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; @@ -27,8 +27,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java index d66c7a9d43a5..013fa369e876 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -23,8 +23,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java index 288ab0ad6596..4c4108a0cb90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DoubleTapClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -27,9 +27,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java index b512f0d6ef32..ee289b5b922d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/PointerCountClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -26,8 +26,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java index c2e290f166a3..38b025f675ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ProximityClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; @@ -28,8 +28,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java index d67f2b833deb..941e12e475f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/SingleTapClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -25,9 +25,6 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java index 1dfffb271f02..6e312594a2e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBufferTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TimeLimitedMotionEventBufferTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java index 5f3b84c2f7ae..6b9bb4fedd16 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; @@ -33,9 +33,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; -import com.android.systemui.classifier.FalsingDataProvider; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java index e49262f5099f..339dd9e9e6d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ZigZagClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; @@ -23,7 +23,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.classifier.ClassifierTest; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java index a6355880c660..37540621557f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java @@ -22,6 +22,7 @@ package com.android.systemui.dock; public class DockManagerFake implements DockManager { DockEventListener mCallback; AlignmentStateListener mAlignmentListener; + private boolean mDocked; @Override public void addListener(DockEventListener callback) { @@ -45,7 +46,11 @@ public class DockManagerFake implements DockManager { @Override public boolean isDocked() { - return false; + return mDocked; + } + + public void setIsDocked(boolean docked) { + mDocked = docked; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java index 3e44fa4a9b2b..477fe6316399 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.people.widget; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static org.mockito.ArgumentMatchers.any; @@ -164,7 +165,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); mNoMan.issueChannelModification(TEST_PACKAGE_A, UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH); @@ -181,7 +182,7 @@ public class PeopleSpaceWidgetManagerTest extends SysuiTestCase { when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray); NotificationChannel channel = - mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME); + new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT); channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID); mNoMan.issueChannelModification(TEST_PACKAGE_A, 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 6fa6f31984f2..fd0715bbca29 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java @@ -15,6 +15,7 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; @@ -24,6 +25,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.pm.UserInfo; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.VectorDrawable; import android.os.Handler; import android.os.Looper; import android.provider.Settings; @@ -75,6 +78,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { private ViewGroup mRootView; private TextView mFooterText; private TestableImageView mFooterIcon; + private TestableImageView mPrimaryFooterIcon; private QSSecurityFooter mFooter; @Mock private SecurityController mSecurityController; @@ -95,6 +99,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mActivityStarter, mSecurityController, looper); mFooterText = mRootView.findViewById(R.id.footer_text); mFooterIcon = mRootView.findViewById(R.id.footer_icon); + mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon); mFooter.setHostEnvironment(null); } @@ -119,6 +124,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); } @@ -136,6 +142,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { mFooterText.getText()); assertEquals(View.VISIBLE, mRootView.getVisibility()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); } @@ -165,6 +172,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); // -1 == never set. assertEquals(-1, mFooterIcon.getLastImageResource()); @@ -203,6 +211,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { VPN_PACKAGE), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); // Same situation, but with organization name set @@ -229,6 +238,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns), mFooterText.getText()); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); // Same situation, but with organization name set @@ -252,6 +262,7 @@ public class QSSecurityFooterTest extends SysuiTestCase { TestableLooper.get(this).processAllMessages(); assertEquals(View.VISIBLE, mFooterIcon.getVisibility()); + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource()); assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring), mFooterText.getText()); @@ -534,12 +545,27 @@ public class QSSecurityFooterTest extends SysuiTestCase { @Test public void testParentalControls() { when(mSecurityController.isParentalControlsEnabled()).thenReturn(true); + + Drawable testDrawable = new VectorDrawable(); + when(mSecurityController.getIcon(any())).thenReturn(testDrawable); + assertNotNull(mSecurityController.getIcon(null)); + mFooter.refreshState(); TestableLooper.get(this).processAllMessages(); assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls), mFooterText.getText()); + assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility()); + + assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable()); + + // Ensure the primary icon is set to gone when parental controls is disabled. + when(mSecurityController.isParentalControlsEnabled()).thenReturn(false); + mFooter.refreshState(); + TestableLooper.get(this).processAllMessages(); + + assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java index 1bfe10c5263b..4507366e3073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.collection; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; - import static org.junit.Assert.assertNotNull; import android.app.NotificationChannel; @@ -76,10 +74,6 @@ public class NoManSimulator { } } - public NotificationChannel createNotificationChannel(String id, String name) { - return new NotificationChannel(id, name, IMPORTANCE_DEFAULT); - } - public void issueChannelModification( String pkg, UserHandle user, NotificationChannel channel, int modificationType) { for (NotificationHandler listener : mListeners) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java index 7f48cd1313fe..edf2b4c30ce4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java @@ -69,32 +69,4 @@ public class NotificationInlineImageResolverTest extends SysuiTestCase { assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); } - - @Test - public void resolveImage_sizeTooBig() throws IOException { - doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); - mResolver.mMaxImageHeight = 5; - mResolver.mMaxImageWidth = 5; - - // original bitmap size is 10x10 - BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); - Bitmap resolvedBitmap = resolved.getBitmap(); - assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth()); - assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight()); - assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap); - } - - @Test - public void resolveImage_sizeOK() throws IOException { - doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); - mResolver.mMaxImageWidth = 15; - mResolver.mMaxImageHeight = 15; - - // original bitmap size is 10x10 - BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); - Bitmap resolvedBitmap = resolved.getBitmap(); - assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth()); - assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight()); - assertSame("Bitmap not replaced", resolvedBitmap, mBitmap); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 3d582e74ea4e..d4a94a19af4f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -63,6 +63,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; +import com.android.systemui.media.MediaDataManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; @@ -197,6 +198,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; @Mock private AuthController mAuthController; + @Mock + private MediaDataManager mMediaDataManager; private NotificationPanelViewController mNotificationPanelViewController; private View.AccessibilityDelegate mAccessibiltyDelegate; @@ -267,7 +270,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mDozeParameters, mCommandQueue, mVibratorHelper, mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, mMetricsLogger, mActivityManager, mConfigurationController, - flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, mConversationNotificationManager, mMediaHiearchyManager, mBiometricUnlockController, mStatusBarKeyguardViewManager, mNotificationStackScrollLayoutController, @@ -275,7 +278,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { mGroupManager, mNotificationAreaController, mAuthController, - new QSDetailDisplayer()); + new QSDetailDisplayer(), + mMediaDataManager); mNotificationPanelViewController.initDependencies( mStatusBar, mNotificationShelfController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 23f263746fb3..eaf31ed17bb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -48,10 +48,10 @@ import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.utils.os.FakeHandler; @@ -99,11 +99,11 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock - private SysuiColorExtractor mSysuiColorExtractor; - @Mock private DockManager mDockManager; @Mock private BlurUtils mBlurUtils; + @Mock + private ConfigurationController mConfigurationController; private static class AnimatorListener implements Animator.AnimatorListener { @@ -196,6 +196,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true); + when(mBlurUtils.supportsBlursOnWindows()).thenReturn(true); doAnswer((Answer<Void>) invocation -> { mScrimState = invocation.getArgument(0); @@ -211,14 +212,12 @@ public class ScrimControllerTest extends SysuiTestCase { .thenReturn(mDelayedWakeLockBuilder); when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock); - when(mSysuiColorExtractor.getNeutralColors()).thenReturn(new GradientColors()); - when(mDockManager.isDocked()).thenReturn(false); mScrimController = new ScrimController(mLightBarController, mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, - new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mSysuiColorExtractor, - mDockManager, mBlurUtils); + new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, + mDockManager, mBlurUtils, mConfigurationController); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mScrimInFront, mScrimForBubble); mScrimController.setAnimatorListener(mAnimatorListener); @@ -520,10 +519,10 @@ public class ScrimControllerTest extends SysuiTestCase { Assert.assertEquals(ScrimController.TRANSPARENT, mScrimInFront.getViewAlpha(), 0.0f); // Back scrim should be visible - Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, + Assert.assertEquals(ScrimController.BLUR_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); // Bubble scrim should be visible - Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA, + Assert.assertEquals(ScrimController.BLUR_SCRIM_ALPHA, mScrimBehind.getViewAlpha(), 0.0f); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index aee884022d95..c826cbcb5389 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -107,7 +107,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { eq(UserHandle.USER_ALL)); verify(mDumpManager).registerDumpable(any(), any()); - List<Integer> colorList = List.of(Color.RED, Color.BLUE); + List<Integer> colorList = List.of(Color.RED, Color.BLUE, 0x0CCCCC, 0x000111); when(mThemeOverlayApplier.getAvailableAccentColors()).thenReturn(colorList); when(mThemeOverlayApplier.getAvailableSystemColors()).thenReturn(colorList); } @@ -148,6 +148,23 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { } @Test + public void onWallpaperColorsChanged_addsLeadingZerosToColors() { + // Should ask for a new theme when wallpaper colors change + WallpaperColors mainColors = new WallpaperColors(Color.valueOf(0x0CCCCC), + Color.valueOf(0x000111), null); + mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM); + ArgumentCaptor<Map<String, String>> themeOverlays = ArgumentCaptor.forClass(Map.class); + + verify(mThemeOverlayApplier).applyCurrentUserOverlays(themeOverlays.capture(), any()); + + // Assert that we received the colors that we were expecting + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE)) + .isEqualTo(MONET_SYSTEM_PALETTE_PACKAGE + "0CCCCC"); + assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_ACCENT_COLOR)) + .isEqualTo(MONET_ACCENT_COLOR_PACKAGE + "000111"); + } + + @Test public void onWallpaperColorsChanged_preservesWallpaperPickerTheme() { // Should ask for a new theme when wallpaper colors change WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED), diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 822a6f2ad810..ef25b73fd748 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,7 +34,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; -import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedGestureHandler; @@ -69,7 +68,6 @@ public class WMShellTest extends SysuiTestCase { @Mock HideDisplayCutout mHideDisplayCutout; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; - @Mock AppPairs mAppPairs; @Before public void setUp() { @@ -79,7 +77,7 @@ public class WMShellTest extends SysuiTestCase { mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer, - Optional.of(mShellCommandHandler), Optional.of(mAppPairs)); + Optional.of(mShellCommandHandler)); when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index be7643ecbd4e..61de53a2a483 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -558,12 +558,13 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo if (mAms.getMagnificationMode(displayId) == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { magnificationGestureHandler = new WindowMagnificationGestureHandler(displayContext, - mAms.getWindowMagnificationMgr(), mAms::onMagnificationScaleChanged, - detectControlGestures, triggerable, displayId); + mAms.getWindowMagnificationMgr(), mAms.getMagnificationController(), + detectControlGestures, triggerable, + displayId); } else { magnificationGestureHandler = new FullScreenMagnificationGestureHandler(displayContext, - mAms.getFullScreenMagnificationController(), - mAms::onMagnificationScaleChanged, detectControlGestures, triggerable, + mAms.getFullScreenMagnificationController(), mAms.getMagnificationController(), + detectControlGestures, triggerable, new WindowMagnificationPromptController(displayContext, mUserId), displayId); } return magnificationGestureHandler; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4c85490a8086..be2f8f16a412 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -119,7 +119,6 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.MagnificationController; -import com.android.server.accessibility.magnification.MagnificationGestureHandler; import com.android.server.accessibility.magnification.WindowMagnificationManager; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -153,7 +152,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, AccessibilitySecurityPolicy.AccessibilityUserManager, - MagnificationGestureHandler.ScaleChangedListener, SystemActionPerformer.SystemActionsChangedListener { private static final boolean DEBUG = false; @@ -1066,17 +1064,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - @Override - public void onMagnificationScaleChanged(int displayId, int mode) { - synchronized (mLock) { - final int capabilities = - getCurrentUserStateLocked().getMagnificationCapabilitiesLocked(); - if (capabilities == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) { - getWindowMagnificationMgr().showMagnificationButton(displayId, mode); - } - } - } - /** * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector. * Not using a getter because the AccessibilityInputFilter isn't thread-safe @@ -3000,6 +2987,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } + /** + * Getter of {@link MagnificationController}. + * + * @return MagnificationController + */ + MagnificationController getMagnificationController() { + synchronized (mLock) { + return mMagnificationController; + } + } + @Override public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) { synchronized (mLock) { @@ -3584,6 +3582,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, userState.mUserId); if (capabilities != userState.getMagnificationCapabilitiesLocked()) { userState.setMagnificationCapabilitiesLocked(capabilities); + mMagnificationController.setMagnificationCapabilities(capabilities); return true; } return false; diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index efb9d87a0bfd..7483ff3ef3f4 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -62,9 +62,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.gestures.GestureUtils; -import java.util.ArrayDeque; -import java.util.Queue; - /** * This class handles full screen magnification in response to touch events. * @@ -115,13 +112,10 @@ import java.util.Queue; */ @SuppressWarnings("WeakerAccess") public class FullScreenMagnificationGestureHandler extends MagnificationGestureHandler { - private static final String LOG_TAG = "FullScreenMagnificationGestureHandler"; - private static final boolean DEBUG_ALL = false; private static final boolean DEBUG_STATE_TRANSITIONS = false | DEBUG_ALL; private static final boolean DEBUG_DETECTING = false | DEBUG_ALL; private static final boolean DEBUG_PANNING_SCALING = false | DEBUG_ALL; - private static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL; // The MIN_SCALE is different from MagnificationController.MIN_SCALE due // to AccessibilityService.MagnificationController#setScale() has @@ -139,41 +133,12 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private final ScreenStateReceiver mScreenStateReceiver; private final WindowMagnificationPromptController mPromptController; - /** - * {@code true} if this detector should detect and respond to triple-tap - * gestures for engaging and disengaging magnification, - * {@code false} if it should ignore such gestures - */ - final boolean mDetectTripleTap; - - /** - * Whether {@link DetectingState#mShortcutTriggered shortcut} is enabled - */ - final boolean mDetectShortcutTrigger; - @VisibleForTesting State mCurrentState; @VisibleForTesting State mPreviousState; private PointerCoords[] mTempPointerCoords; private PointerProperties[] mTempPointerProperties; - private final int mDisplayId; - - private final Queue<MotionEvent> mDebugInputEventHistory; - private final Queue<MotionEvent> mDebugOutputEventHistory; - - /** - * @param context Context for resolving various magnification-related resources - * @param fullScreenMagnificationController the {@link FullScreenMagnificationController} - * - * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap - * gestures for engaging and disengaging magnification, - * {@code false} if it should ignore such gestures - * @param detectShortcutTrigger {@code true} if this detector should be "triggerable" by some - * external shortcut invoking {@link #notifyShortcutTriggered}, - * {@code false} if it should ignore such triggers. - * @param displayId The logical display id. - */ public FullScreenMagnificationGestureHandler(Context context, FullScreenMagnificationController fullScreenMagnificationController, ScaleChangedListener listener, @@ -181,23 +146,20 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH boolean detectShortcutTrigger, @NonNull WindowMagnificationPromptController promptController, int displayId) { - super(listener); + super(displayId, detectTripleTap, detectShortcutTrigger, listener); if (DEBUG_ALL) { - Log.i(LOG_TAG, + Log.i(mLogTag, "FullScreenMagnificationGestureHandler(detectTripleTap = " + detectTripleTap + ", detectShortcutTrigger = " + detectShortcutTrigger + ")"); } mFullScreenMagnificationController = fullScreenMagnificationController; mPromptController = promptController; - mDisplayId = displayId; mDelegatingState = new DelegatingState(); mDetectingState = new DetectingState(context); mViewportDraggingState = new ViewportDraggingState(); mPanningScalingState = new PanningScalingState(context); - mDetectTripleTap = detectTripleTap; - mDetectShortcutTrigger = detectShortcutTrigger; if (mDetectShortcutTrigger) { mScreenStateReceiver = new ScreenStateReceiver(context, this); mScreenStateReceiver.register(); @@ -205,36 +167,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mScreenStateReceiver = null; } - mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; - mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; - transitionTo(mDetectingState); } @Override - public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (DEBUG_EVENT_STREAM) { - storeEventInto(mDebugInputEventHistory, event); - try { - onMotionEventInternal(event, rawEvent, policyFlags); - } catch (Exception e) { - throw new RuntimeException( - "Exception following input events: " + mDebugInputEventHistory, e); - } - } else { - onMotionEventInternal(event, rawEvent, policyFlags); - } - } - - private void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (DEBUG_ALL) Slog.i(LOG_TAG, "onMotionEvent(" + event + ")"); - - if ((!mDetectTripleTap && !mDetectShortcutTrigger) - || !event.isFromSource(SOURCE_TOUCHSCREEN)) { - dispatchTransformedEvent(event, rawEvent, policyFlags); - return; - } - + void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { handleEventWith(mCurrentState, event, rawEvent, policyFlags); } @@ -259,7 +196,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @Override public void onDestroy() { if (DEBUG_STATE_TRANSITIONS) { - Slog.i(LOG_TAG, "onDestroy(); delayed = " + Slog.i(mLogTag, "onDestroy(); delayed = " + MotionEventInfo.toString(mDetectingState.mDelayedEventQueue)); } @@ -299,31 +236,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mPanningScalingState.clear(); } - private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, - int policyFlags) { - if (DEBUG_EVENT_STREAM) { - storeEventInto(mDebugOutputEventHistory, event); - try { - super.onMotionEvent(event, rawEvent, policyFlags); - } catch (Exception e) { - throw new RuntimeException( - "Exception downstream following input events: " + mDebugInputEventHistory - + "\nTransformed into output events: " + mDebugOutputEventHistory, - e); - } - } else { - super.onMotionEvent(event, rawEvent, policyFlags); - } - } - - private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) { - queue.add(MotionEvent.obtain(event)); - // Prune old events - while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) { - queue.remove().recycle(); - } - } - private PointerCoords[] getTempPointerCoordsWithMinSize(int size) { final int oldSize = (mTempPointerCoords != null) ? mTempPointerCoords.length : 0; if (oldSize < size) { @@ -358,7 +270,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private void transitionTo(State state) { if (DEBUG_STATE_TRANSITIONS) { - Slog.i(LOG_TAG, + Slog.i(mLogTag, (State.nameOf(mCurrentState) + " -> " + State.nameOf(state) + " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5))) .replace(getClass().getName(), "")); @@ -440,7 +352,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH return true; } if (DEBUG_PANNING_SCALING) { - Slog.i(LOG_TAG, "Panned content by scrollX: " + distanceX + Slog.i(mLogTag, "Panned content by scrollX: " + distanceX + " scrollY: " + distanceY); } mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX, @@ -480,7 +392,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH final float pivotX = detector.getFocusX(); final float pivotY = detector.getFocusY(); - if (DEBUG_PANNING_SCALING) Slog.i(LOG_TAG, "Scaled content to: " + scale + "x"); + if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x"); mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); mListener.onMagnificationScaleChanged(mDisplayId, getMode()); @@ -945,7 +857,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private void onTripleTap(MotionEvent up) { if (DEBUG_DETECTING) { - Slog.i(LOG_TAG, "onTripleTap(); delayed: " + Slog.i(mLogTag, "onTripleTap(); delayed: " + MotionEventInfo.toString(mDelayedEventQueue)); } clear(); @@ -965,7 +877,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH void transitionToViewportDraggingStateAndClear(MotionEvent down) { - if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()"); + if (DEBUG_DETECTING) Slog.i(mLogTag, "onTripleTapAndHold()"); clear(); mViewportDraggingState.mZoomedInBeforeDrag = @@ -997,7 +909,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (mShortcutTriggered == state) { return; } - if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")"); + if (DEBUG_DETECTING) Slog.i(mLogTag, "setShortcutTriggered(" + state + ")"); mShortcutTriggered = state; mFullScreenMagnificationController.setForceShowMagnifiableBounds(mDisplayId, state); @@ -1030,7 +942,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } private void zoomOn(float centerX, float centerY) { - if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOn(" + centerX + ", " + centerY + ")"); + if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOn(" + centerX + ", " + centerY + ")"); final float scale = MathUtils.constrain( mFullScreenMagnificationController.getPersistedScale(), @@ -1042,7 +954,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } private void zoomOff() { - if (DEBUG_DETECTING) Slog.i(LOG_TAG, "zoomOff()"); + if (DEBUG_DETECTING) Slog.i(mLogTag, "zoomOff()"); mFullScreenMagnificationController.reset(mDisplayId, /* animate */ true); } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index 6f81b5cfe3e6..df88ceb95d9e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -37,7 +37,8 @@ import com.android.server.accessibility.AccessibilityManagerService; * Handles all magnification controllers initialization, generic interactions * and magnification mode transition. */ -public class MagnificationController implements WindowMagnificationManager.Callback { +public class MagnificationController implements WindowMagnificationManager.Callback, + MagnificationGestureHandler.ScaleChangedListener { private static final boolean DEBUG = false; private static final String TAG = "MagnificationController"; @@ -50,6 +51,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb private FullScreenMagnificationController mFullScreenMagnificationController; private WindowMagnificationManager mWindowMagnificationMgr; + private int mMagnificationCapabilities = ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; /** * A callback to inform the magnification transition result. @@ -82,10 +84,18 @@ public class MagnificationController implements WindowMagnificationManager.Callb public void onPerformScaleAction(int displayId, float scale) { getWindowMagnificationMgr().setScale(displayId, scale); getWindowMagnificationMgr().persistScale(displayId); - mAms.onMagnificationScaleChanged(displayId, + onMagnificationScaleChanged(displayId, Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); } + @Override + public void onMagnificationScaleChanged(int displayId, int mode) { + if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) { + return; + } + getWindowMagnificationMgr().showMagnificationButton(displayId, mode); + } + /** * Transitions to the target Magnification mode with current center of the magnification mode * if it is available. @@ -182,6 +192,10 @@ public class MagnificationController implements WindowMagnificationManager.Callb } } + public void setMagnificationCapabilities(int capabilities) { + mMagnificationCapabilities = capabilities; + } + private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked( int displayId) { return mMagnificationEndRunnableSparseArray.get(displayId); diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java index d6f53d2c225c..386d0bbf35ec 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java @@ -16,32 +16,127 @@ package com.android.server.accessibility.magnification; +import static android.view.InputDevice.SOURCE_TOUCHSCREEN; + +import android.annotation.NonNull; +import android.util.Log; +import android.util.Slog; +import android.view.MotionEvent; + import com.android.server.accessibility.BaseEventStreamTransformation; +import java.util.ArrayDeque; +import java.util.Queue; + /** * A base class that detects gestures and defines common methods for magnification. */ public abstract class MagnificationGestureHandler extends BaseEventStreamTransformation { - protected final ScaleChangedListener mListener; + protected final String mLogTag = this.getClass().getSimpleName(); + protected static final boolean DEBUG_ALL = Log.isLoggable("MagnificationGestureHandler", + Log.DEBUG); + protected static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL; + private final Queue<MotionEvent> mDebugInputEventHistory; + private final Queue<MotionEvent> mDebugOutputEventHistory; - protected MagnificationGestureHandler(ScaleChangedListener listener) { - mListener = listener; - } + /** + * The logical display id. + */ + protected final int mDisplayId; /** - * Interface for listening to the magnification scaling gesture. + * {@code true} if this detector should be "triggerable" by some + * external shortcut invoking {@link #notifyShortcutTriggered}, + * {@code false} if it should ignore such triggers. */ + protected final boolean mDetectShortcutTrigger; + + /** + * {@code true} if this detector should detect and respond to triple-tap + * gestures for engaging and disengaging magnification, + * {@code false} if it should ignore such gestures + */ + protected final boolean mDetectTripleTap; + + /** Interface for listening to the magnification scaling gesture. */ public interface ScaleChangedListener { /** * Called when the magnification scale is changed by users. * * @param displayId The logical display id - * @param mode The magnification mode + * @param mode The magnification mode */ void onMagnificationScaleChanged(int displayId, int mode); } + protected final ScaleChangedListener mListener; + + protected MagnificationGestureHandler(int displayId, boolean detectTripleTap, + boolean detectShortcutTrigger, + @NonNull ScaleChangedListener listener) { + mDisplayId = displayId; + mDetectTripleTap = detectTripleTap; + mDetectShortcutTrigger = detectShortcutTrigger; + mListener = listener; + + mDebugInputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; + mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; + } + + @Override + public final void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + if (DEBUG_ALL) { + Slog.i(mLogTag, "onMotionEvent(" + event + ")"); + } + if (DEBUG_EVENT_STREAM) { + storeEventInto(mDebugInputEventHistory, event); + } + if (shouldDispatchTransformedEvent(event)) { + dispatchTransformedEvent(event, rawEvent, policyFlags); + } else { + onMotionEventInternal(event, rawEvent, policyFlags); + } + } + + private boolean shouldDispatchTransformedEvent(MotionEvent event) { + if ((!mDetectTripleTap && !mDetectShortcutTrigger) || !event.isFromSource( + SOURCE_TOUCHSCREEN)) { + return true; + } + return false; + } + + final void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, + int policyFlags) { + if (DEBUG_EVENT_STREAM) { + storeEventInto(mDebugOutputEventHistory, event); + try { + super.onMotionEvent(event, rawEvent, policyFlags); + return; + } catch (Exception e) { + throw new RuntimeException( + "Exception downstream following input events: " + mDebugInputEventHistory + + "\nTransformed into output events: " + mDebugOutputEventHistory, + e); + } + } + super.onMotionEvent(event, rawEvent, policyFlags); + } + + private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) { + queue.add(MotionEvent.obtain(event)); + // Prune old events + while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) { + queue.remove().recycle(); + } + } + + /** + * Called when this MagnificationGestureHandler handles the motion event. + */ + abstract void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags); + /** * Called when the shortcut target is magnification. */ @@ -51,7 +146,6 @@ public abstract class MagnificationGestureHandler extends BaseEventStreamTransfo * Indicates the magnification mode. * * @return the magnification mode of the handler - * * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN * @see android.provider.Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW */ diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java index 7d6067c8e1da..7f26b2755900 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java @@ -27,7 +27,6 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.Point; import android.provider.Settings; -import android.util.Log; import android.util.MathUtils; import android.util.Slog; import android.view.Display; @@ -38,9 +37,7 @@ import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.gestures.MultiTap; import com.android.server.accessibility.gestures.MultiTapAndHold; -import java.util.ArrayDeque; import java.util.List; -import java.util.Queue; /** * This class handles window magnification in response to touch events and shortcut. @@ -64,12 +61,9 @@ import java.util.Queue; */ @SuppressWarnings("WeakerAccess") public class WindowMagnificationGestureHandler extends MagnificationGestureHandler { - private static final String LOG_TAG = "WindowMagnificationGestureHandler"; - private static final boolean DEBUG_ALL = Log.isLoggable(LOG_TAG, Log.DEBUG); private static final boolean DEBUG_STATE_TRANSITIONS = false | DEBUG_ALL; private static final boolean DEBUG_DETECTING = false | DEBUG_ALL; - private static final boolean DEBUG_EVENT_STREAM = false | DEBUG_ALL; //Ensure the range has consistency with FullScreenMagnificationGestureHandler. private static final float MIN_SCALE = 2.0f; @@ -88,38 +82,26 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @VisibleForTesting State mPreviousState; - final boolean mDetectShortcutTrigger; - private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate; - private final int mDisplayId; private final Context mContext; private final Point mTempPoint = new Point(); - private final Queue<MotionEvent> mDebugOutputEventHistory; - - /** - * @param context Context for resolving various magnification-related resources - * @param windowMagnificationMgr The {@link WindowMagnificationManager} - * @param displayId The logical display id. - */ public WindowMagnificationGestureHandler(Context context, WindowMagnificationManager windowMagnificationMgr, - ScaleChangedListener listener, boolean detectTripleTap, - boolean detectShortcutTrigger, int displayId) { - super(listener); + ScaleChangedListener listener, + boolean detectTripleTap, boolean detectShortcutTrigger, int displayId) { + super(displayId, detectTripleTap, detectShortcutTrigger, listener); if (DEBUG_ALL) { - Slog.i(LOG_TAG, + Slog.i(mLogTag, "WindowMagnificationGestureHandler() , displayId = " + displayId + ")"); } mContext = context; mWindowMagnificationMgr = windowMagnificationMgr; - mDetectShortcutTrigger = detectShortcutTrigger; - mDisplayId = displayId; mMotionEventDispatcherDelegate = new MotionEventDispatcherDelegate(context, - (event, rawEvent, policyFlags) -> super.onMotionEvent( - event, rawEvent, policyFlags)); + (event, rawEvent, policyFlags) -> dispatchTransformedEvent(event, rawEvent, + policyFlags)); mDelegatingState = new DelegatingState(mMotionEventDispatcherDelegate); - mDetectingState = new DetectingState(context, detectTripleTap); + mDetectingState = new DetectingState(context, mDetectTripleTap); mObservePanningScalingState = new PanningScalingGestureState( new PanningScalingHandler(context, MAX_SCALE, MIN_SCALE, true, new PanningScalingHandler.MagnificationDelegate() { @@ -142,24 +124,14 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl } })); - mDebugOutputEventHistory = DEBUG_EVENT_STREAM ? new ArrayDeque<>() : null; - transitionTo(mDetectingState); } @Override - public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - if (DEBUG_ALL) { - Slog.i(LOG_TAG, "onMotionEvent(" + event + ")"); - } - if (!event.isFromSource(SOURCE_TOUCHSCREEN)) { - dispatchTransformedEvent(event, rawEvent, policyFlags); - return; - } else { - // To keep InputEventConsistencyVerifiers within GestureDetectors happy. - mObservePanningScalingState.mPanningScalingHandler.onTouchEvent(event); - mCurrentState.onMotionEvent(event, rawEvent, policyFlags); - } + void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + // To keep InputEventConsistencyVerifiers within GestureDetectors happy. + mObservePanningScalingState.mPanningScalingHandler.onTouchEvent(event); + mCurrentState.onMotionEvent(event, rawEvent, policyFlags); } @Override @@ -173,7 +145,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @Override public void onDestroy() { if (DEBUG_ALL) { - Slog.i(LOG_TAG, "onDestroy(); delayed = " + Slog.i(mLogTag, "onDestroy(); delayed = " + mDetectingState.toString()); } mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true); @@ -183,7 +155,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl @Override public void notifyShortcutTriggered() { if (DEBUG_ALL) { - Slog.i(LOG_TAG, "notifyShortcutTriggered():"); + Slog.i(mLogTag, "notifyShortcutTriggered():"); } if (!mDetectShortcutTrigger) { return; @@ -205,7 +177,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private void enableWindowMagnifier(float centerX, float centerY) { if (DEBUG_ALL) { - Slog.i(LOG_TAG, "enableWindowMagnifier :" + centerX + ", " + centerY); + Slog.i(mLogTag, "enableWindowMagnifier :" + centerX + ", " + centerY); } final float scale = MathUtils.constrain( @@ -216,7 +188,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private void disableWindowMagnifier() { if (DEBUG_ALL) { - Slog.i(LOG_TAG, "disableWindowMagnifier()"); + Slog.i(mLogTag, "disableWindowMagnifier()"); } mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false); } @@ -231,7 +203,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private void onTripleTap(MotionEvent up) { if (DEBUG_DETECTING) { - Slog.i(LOG_TAG, "onTripleTap()"); + Slog.i(mLogTag, "onTripleTap()"); } toggleMagnification(up.getX(), up.getY()); } @@ -240,30 +212,6 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl transitionTo(mDetectingState); } - private void dispatchTransformedEvent(MotionEvent event, MotionEvent rawEvent, - int policyFlags) { - if (DEBUG_EVENT_STREAM) { - storeEventInto(mDebugOutputEventHistory, event); - try { - super.onMotionEvent(event, rawEvent, policyFlags); - } catch (Exception e) { - throw new RuntimeException( - "Exception downstream following input events: " + mDebugOutputEventHistory, - e); - } - } else { - super.onMotionEvent(event, rawEvent, policyFlags); - } - } - - private static void storeEventInto(Queue<MotionEvent> queue, MotionEvent event) { - queue.add(MotionEvent.obtain(event)); - // Prune old events. - while (!queue.isEmpty() && (event.getEventTime() - queue.peek().getEventTime() > 5000)) { - queue.remove().recycle(); - } - } - /** * An interface to intercept the {@link MotionEvent} for gesture detection. The intercepted * events should be delivered to next {@link EventStreamTransformation} with { @@ -293,7 +241,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl private void transitionTo(State state) { if (DEBUG_STATE_TRANSITIONS) { - Slog.i(LOG_TAG, "state transition: " + (State.nameOf(mCurrentState) + " -> " + Slog.i(mLogTag, "state transition: " + (State.nameOf(mCurrentState) + " -> " + State.nameOf(state) + " at " + asList(copyOfRange(new RuntimeException().getStackTrace(), 1, 5))) .replace(getClass().getName(), "")); @@ -451,10 +399,10 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl List<MotionEventInfo> delayedEventQueue, MotionEvent motionEvent) { if (DEBUG_DETECTING) { - Slog.d(LOG_TAG, "onGestureDetected : gesture = " + Slog.d(mLogTag, "onGestureDetected : gesture = " + MagnificationGestureMatcher.gestureIdToString( gestureId)); - Slog.d(LOG_TAG, + Slog.d(mLogTag, "onGestureDetected : delayedEventQueue = " + delayedEventQueue); } if (gestureId == MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN @@ -474,7 +422,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl List<MotionEventInfo> delayedEventQueue, MotionEvent motionEvent) { if (DEBUG_DETECTING) { - Slog.d(LOG_TAG, + Slog.d(mLogTag, "onGestureCancelled : delayedEventQueue = " + delayedEventQueue); } mMotionEventDispatcherDelegate.sendDelayedMotionEvents(delayedEventQueue, diff --git a/services/autofill/java/com/android/server/autofill/TEST_MAPPING b/services/autofill/java/com/android/server/autofill/TEST_MAPPING new file mode 100644 index 000000000000..cf058add0262 --- /dev/null +++ b/services/autofill/java/com/android/server/autofill/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "CtsAutoFillServiceTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +} diff --git a/services/core/Android.bp b/services/core/Android.bp index fec7ac0dd5bd..17e3456e565b 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -145,6 +145,7 @@ java_library_static { "SurfaceFlingerProperties", "com.android.sysprop.watchdog", ], + javac_shard_size: 50, } java_genrule { @@ -188,6 +189,13 @@ prebuilt_etc { src: ":services.core.json.gz", } +filegroup { + name: "services.core-sources-deviceconfig-interface", + srcs: [ + "java/com/android/server/utils/DeviceConfigInterface.java" + ], +} + // TODO: Move connectivity service sources to independent directory. filegroup { name: "connectivity-service-srcs", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 031cc42f99b9..f0677a23765c 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -41,6 +41,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; @@ -4821,15 +4822,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) { - ensureRunningOnConnectivityServiceThread(); - NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId()); - if (vpnNai == null || nc == null) { - return; - } - updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc); - } - @Override public boolean updateLockdownVpn() { if (Binder.getCallingUid() != Process.SYSTEM_UID) { @@ -5140,7 +5132,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStart(int userId) { + private void onUserStarted(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn != null) { @@ -5155,7 +5147,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private void onUserStop(int userId) { + private void onUserStopped(int userId) { synchronized (mVpns) { Vpn userVpn = mVpns.get(userId); if (userVpn == null) { @@ -5169,28 +5161,22 @@ public class ConnectivityService extends IConnectivityManager.Stub private void onUserAdded(int userId) { mPermissionMonitor.onUserAdded(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserAdded(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } private void onUserRemoved(int userId) { mPermissionMonitor.onUserRemoved(userId); - Network defaultNetwork = getNetwork(getDefaultNetwork()); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { Vpn vpn = mVpns.valueAt(i); vpn.onUserRemoved(userId); - NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork); - updateVpnCapabilities(vpn, nc); } } } @@ -5272,9 +5258,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { - onUserStart(userId); + onUserStarted(userId); } else if (Intent.ACTION_USER_STOPPED.equals(action)) { - onUserStop(userId); + onUserStopped(userId); } else if (Intent.ACTION_USER_ADDED.equals(action)) { onUserAdded(userId); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { @@ -6369,18 +6355,71 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED); } - /** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */ - private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) { - Network[] underlyingNetworks = nai.declaredUnderlyingNetworks; - Network defaultNetwork = getNetwork(getDefaultNetwork()); + /** Modifies |caps| based on the capabilities of the specified underlying networks. */ + @VisibleForTesting + void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks, + @NonNull NetworkCapabilities caps, boolean declaredMetered) { + final Network defaultNetwork = getNetwork(getDefaultNetwork()); if (underlyingNetworks == null && defaultNetwork != null) { // null underlying networks means to track the default. underlyingNetworks = new Network[] { defaultNetwork }; } + int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; + int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; + int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; + boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered + boolean roaming = false; // roaming if any underlying is roaming + boolean congested = false; // congested if any underlying is congested + boolean suspended = true; // suspended if all underlying are suspended + + boolean hadUnderlyingNetworks = false; + if (null != underlyingNetworks) { + for (Network underlyingNetwork : underlyingNetworks) { + final NetworkAgentInfo underlying = + getNetworkAgentInfoForNetwork(underlyingNetwork); + if (underlying == null) continue; + + final NetworkCapabilities underlyingCaps = underlying.networkCapabilities; + hadUnderlyingNetworks = true; + for (int underlyingType : underlyingCaps.getTransportTypes()) { + transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); + } - // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. - final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class); - Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered); + // Merge capabilities of this underlying network. For bandwidth, assume the + // worst case. + downKbps = NetworkCapabilities.minBandwidth(downKbps, + underlyingCaps.getLinkDownstreamBandwidthKbps()); + upKbps = NetworkCapabilities.minBandwidth(upKbps, + underlyingCaps.getLinkUpstreamBandwidthKbps()); + // If this underlying network is metered, the VPN is metered (it may cost money + // to send packets on this network). + metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED); + // If this underlying network is roaming, the VPN is roaming (the billing structure + // is different than the usual, local one). + roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING); + // If this underlying network is congested, the VPN is congested (the current + // condition of the network affects the performance of this network). + congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); + // If this network is not suspended, the VPN is not suspended (the VPN + // is able to transfer some data). + suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); + } + } + if (!hadUnderlyingNetworks) { + // No idea what the underlying networks are; assume reasonable defaults + metered = true; + roaming = false; + congested = false; + suspended = false; + } + + caps.setTransportTypes(transportTypes); + caps.setLinkDownstreamBandwidthKbps(downKbps); + caps.setLinkUpstreamBandwidthKbps(upKbps); + caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered); + caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming); + caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested); + caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended); } /** @@ -6437,7 +6476,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } if (nai.supportsUnderlyingNetworks()) { - mixInUnderlyingCapabilities(nai, newNc); + applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered); } return newNc; @@ -8276,13 +8315,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return false; } - final Network[] underlyingNetworks; - synchronized (mVpns) { - final Vpn vpn = getVpnIfOwner(callbackUid); - underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks(); - } - if (underlyingNetworks != null) { - if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true; + for (NetworkAgentInfo virtual : mNetworkAgentInfos.values()) { + if (virtual.supportsUnderlyingNetworks() + && virtual.networkCapabilities.getOwnerUid() == callbackUid + && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + return true; + } } // Administrator UIDs also contains the Owner UID diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index f3c5fd8d1064..9bf63cbbb25e 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -1043,7 +1043,7 @@ public class PackageWatchdog { TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, TAG_PACKAGE_WATCHDOG); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); + out.attributeInt(null, ATTR_VERSION, DB_VERSION); for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { mAllObservers.valueAt(oIndex).writeLocked(out); } @@ -1107,7 +1107,7 @@ public class PackageWatchdog { * Does not persist any package failure thresholds. */ @GuardedBy("mLock") - public boolean writeLocked(XmlSerializer out) { + public boolean writeLocked(TypedXmlSerializer out) { try { out.startTag(null, TAG_OBSERVER); out.attribute(null, ATTR_NAME, name); @@ -1225,7 +1225,7 @@ public class PackageWatchdog { * #loadFromFile which in turn is only called on construction of the * singleton PackageWatchdog. **/ - public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) { + public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) { String observerName = null; if (TAG_OBSERVER.equals(parser.getName())) { observerName = parser.getAttributeValue(null, ATTR_NAME); @@ -1240,14 +1240,14 @@ public class PackageWatchdog { while (XmlUtils.nextElementWithin(parser, innerDepth)) { if (TAG_PACKAGE.equals(parser.getName())) { try { - String packageName = parser.getAttributeValue(null, ATTR_NAME); - long duration = Long.parseLong( - parser.getAttributeValue(null, ATTR_DURATION)); - long healthCheckDuration = Long.parseLong( - parser.getAttributeValue(null, - ATTR_EXPLICIT_HEALTH_CHECK_DURATION)); - boolean hasPassedHealthCheck = Boolean.parseBoolean( - parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK)); + String packageName = parser.getAttributeValue( + null, ATTR_NAME); + long duration = parser.getAttributeLong( + null, ATTR_DURATION); + long healthCheckDuration = parser.getAttributeLong( + null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION); + boolean hasPassedHealthCheck = parser.getAttributeBoolean( + null, ATTR_PASSED_HEALTH_CHECK, false); MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName, duration, healthCheckDuration, hasPassedHealthCheck); if (pkg != null) { @@ -1364,14 +1364,12 @@ public class PackageWatchdog { /** Writes the salient fields to disk using {@code out}. */ @GuardedBy("mLock") - public void writeLocked(XmlSerializer out) throws IOException { + public void writeLocked(TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATTR_NAME, getName()); - out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs)); - out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, - String.valueOf(mHealthCheckDurationMs)); - out.attribute(null, ATTR_PASSED_HEALTH_CHECK, - String.valueOf(mHasPassedHealthCheck)); + out.attributeLong(null, ATTR_DURATION, mDurationMs); + out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs); + out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck); out.endTag(null, TAG_PACKAGE); } diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 2455e76d69a8..d30e9fb002e0 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -107,7 +107,7 @@ public final class SensorPrivacyService extends SystemService { TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable)); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enable); serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); serializer.endDocument(); mAtomicFile.finishWrite(outputStream); @@ -180,7 +180,7 @@ public final class SensorPrivacyService extends SystemService { TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream); serializer.startDocument(null, true); serializer.startTag(null, XML_TAG_SENSOR_PRIVACY); - serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled)); + serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled); serializer.endTag(null, XML_TAG_SENSOR_PRIVACY); serializer.endDocument(); mAtomicFile.finishWrite(outputStream); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 04a52e049474..dbd27af49b17 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -38,13 +38,8 @@ import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED; import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED; import static android.os.storage.OnObbStateChangeListener.MOUNTED; import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; -import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; -import static com.android.internal.util.XmlUtils.readIntAttribute; -import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; -import static com.android.internal.util.XmlUtils.writeIntAttribute; -import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -144,7 +139,6 @@ import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -160,9 +154,7 @@ import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver; import libcore.io.IoUtils; import libcore.util.EmptyArray; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -172,7 +164,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.math.BigInteger; -import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.spec.KeySpec; import java.util.ArrayList; @@ -186,7 +177,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -212,21 +202,34 @@ class StorageManagerService extends IStorageManager.Stub private static final String ZRAM_ENABLED_PROPERTY = "persist.sys.zram_enabled"; - private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); - // A system property to control if obb app data isolation is enabled in vold. private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = "persist.sys.vold_app_data_isolation_enabled"; + // TODO(b/169327180): Will be fetched from the server, but for now, we emulate this in + // the system_server since it can write to DeviceConfig and MediaProvider can read it + private static final String PROP_TRANSCODE_ENABLED = "transcode_enabled"; + private static final String PROP_TRANSCODE_DEFAULT = "transcode_default"; + private static final String PROP_TRANSCODE_COMPAT_MANIFEST = "transcode_compat_manifest"; + private static final boolean TRANSCODE_ENABLED_VALUE = false; + // Determines the default behavior of apps when transcode is enabled, AKA, Option A/Option B. + // If true, transcode by default (Option B). If false, don't transcode by default (Option A) + // For dogfood, we go with Option B + private static final boolean TRANSCODE_DEFAULT_VALUE = true; + // Format is <package_name>,<media_capability_bit_mask>,... + // media_capability_bit_mask is defined in MediaProvider/../TranscodeHelper.java: + // FLAG_HEVC = 1 << 0; + // FLAG_SLOW_MOTION = 1 << 1; + // FLAG_HDR_10 = 1 << 2; + // FLAG_HDR_10_PLUS = 1 << 3; + // FLAG_HDR_HLG = 1 << 4; + // FLAG_HDR_DOLBY_VISION = 1 << 5; + private static final String TRANSCODE_COMPAT_MANIFEST_VALUE = + "com.google.android.apps.photos,1"; + // How long we wait to reset storage, if we failed to call onMount on the // external storage service. public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10; - /** - * If {@code 1}, enables the isolated storage feature. If {@code -1}, - * disables the isolated storage feature. If {@code 0}, uses the default - * value from the build system. - */ - private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; @GuardedBy("mLock") private final Set<Integer> mFuseMountedUser = new ArraySet<>(); @@ -898,22 +901,18 @@ class StorageManagerService extends IStorageManager.Stub com.android.internal.R.bool.config_zramWriteback)) { ZramWriteback.scheduleZramWriteback(mContext); } - // Toggle isolated-enable system property in response to settings - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE), - false /*notifyForDescendants*/, - new ContentObserver(null /* current thread */) { - @Override - public void onChange(boolean selfChange) { - refreshIsolatedStorageSettings(); - } - }); - // For now, simply clone property when it changes - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - mContext.getMainExecutor(), (properties) -> { - refreshIsolatedStorageSettings(); - }); - refreshIsolatedStorageSettings(); + + // TODO(b/169327180): Remove after setting up server-side DeviceConfig flags + // Set DeviceConfig values for transcoding that will be read by MediaProvider + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_ENABLED, String.valueOf(TRANSCODE_ENABLED_VALUE), + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_DEFAULT, String.valueOf(TRANSCODE_DEFAULT_VALUE), + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, + PROP_TRANSCODE_COMPAT_MANIFEST, TRANSCODE_COMPAT_MANIFEST_VALUE, + false /* makeDefault */); } /** @@ -945,38 +944,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private void refreshIsolatedStorageSettings() { - // Always copy value from newer DeviceConfig location - Settings.Global.putString(mResolver, - Settings.Global.ISOLATED_STORAGE_REMOTE, - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - ISOLATED_STORAGE_ENABLED)); - - final int local = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_LOCAL, 0); - final int remote = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_REMOTE, 0); - - // Walk down precedence chain; we prefer local settings first, then - // remote settings, before finally falling back to hard-coded default. - final boolean res; - if (local == -1) { - res = false; - } else if (local == 1) { - res = true; - } else if (remote == -1) { - res = false; - } else if (remote == 1) { - res = true; - } else { - res = true; - } - - Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " - + remote + " resolved to " + res); - SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res)); - } - /** * MediaProvider has a ton of code that makes assumptions about storage * paths never changing, so we outright kill them to pick up new state. @@ -1345,12 +1312,13 @@ class StorageManagerService extends IStorageManager.Stub final int oldState = vol.state; final int newState = state; vol.state = newState; + final VolumeInfo vInfo = new VolumeInfo(vol); final SomeArgs args = SomeArgs.obtain(); - args.arg1 = vol; + args.arg1 = vInfo; args.arg2 = oldState; args.arg3 = newState; mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget(); - onVolumeStateChangedLocked(vol, oldState, newState); + onVolumeStateChangedLocked(vInfo, oldState, newState); } } } @@ -1762,11 +1730,6 @@ class StorageManagerService extends IStorageManager.Stub */ public StorageManagerService(Context context) { sSelf = this; - - // Snapshot feature flag used for this boot - SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( - SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); - mVoldAppDataIsolationEnabled = SystemProperties.getBoolean( ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); mContext = context; @@ -2032,7 +1995,7 @@ class StorageManagerService extends IStorageManager.Stub if (type == START_TAG) { final String tag = in.getName(); if (TAG_VOLUMES.equals(tag)) { - final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); + final int version = in.getAttributeInt(null, ATTR_VERSION, VERSION_INIT); final boolean primaryPhysical = SystemProperties.getBoolean( StorageManager.PROP_PRIMARY_PHYSICAL, false); final boolean validAttr = (version >= VERSION_FIX_PRIMARY) @@ -2067,7 +2030,7 @@ class StorageManagerService extends IStorageManager.Stub TypedXmlSerializer out = Xml.resolveSerializer(fos); out.startDocument(null, true); out.startTag(null, TAG_VOLUMES); - writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); + out.attributeInt(null, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); final int size = mRecords.size(); for (int i = 0; i < size; i++) { @@ -2085,31 +2048,33 @@ class StorageManagerService extends IStorageManager.Stub } } - public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { - final int type = readIntAttribute(in, ATTR_TYPE); + public static VolumeRecord readVolumeRecord(TypedXmlPullParser in) + throws IOException, XmlPullParserException { + final int type = in.getAttributeInt(null, ATTR_TYPE); final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); final VolumeRecord meta = new VolumeRecord(type, fsUuid); meta.partGuid = readStringAttribute(in, ATTR_PART_GUID); meta.nickname = readStringAttribute(in, ATTR_NICKNAME); - meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); - meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0); - meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0); - meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0); - meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0); + meta.userFlags = in.getAttributeInt(null, ATTR_USER_FLAGS); + meta.createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS, 0); + meta.lastSeenMillis = in.getAttributeLong(null, ATTR_LAST_SEEN_MILLIS, 0); + meta.lastTrimMillis = in.getAttributeLong(null, ATTR_LAST_TRIM_MILLIS, 0); + meta.lastBenchMillis = in.getAttributeLong(null, ATTR_LAST_BENCH_MILLIS, 0); return meta; } - public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { + public static void writeVolumeRecord(TypedXmlSerializer out, VolumeRecord rec) + throws IOException { out.startTag(null, TAG_VOLUME); - writeIntAttribute(out, ATTR_TYPE, rec.type); + out.attributeInt(null, ATTR_TYPE, rec.type); writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid); writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); - writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); - writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis); - writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis); - writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); - writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); + out.attributeInt(null, ATTR_USER_FLAGS, rec.userFlags); + out.attributeLong(null, ATTR_CREATED_MILLIS, rec.createdMillis); + out.attributeLong(null, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis); + out.attributeLong(null, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); + out.attributeLong(null, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); out.endTag(null, TAG_VOLUME); } @@ -2604,32 +2569,6 @@ class StorageManagerService extends IStorageManager.Stub Binder.restoreCallingIdentity(token); } } - - if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON - | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) { - final int value; - if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) { - value = 1; - } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) { - value = -1; - } else { - value = 0; - } - - final long token = Binder.clearCallingIdentity(); - try { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.ISOLATED_STORAGE_LOCAL, value); - refreshIsolatedStorageSettings(); - - // Perform hard reboot to kick policy into place - mHandler.post(() -> { - mContext.getSystemService(PowerManager.class).reboot(null); - }); - } finally { - Binder.restoreCallingIdentity(token); - } - } } @Override @@ -4149,15 +4088,6 @@ class StorageManagerService extends IStorageManager.Stub } } - private int getMountMode(int uid, String packageName) { - final int mode = getMountModeInternal(uid, packageName); - if (LOCAL_LOGV) { - Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/" - + UserHandle.formatUid(uid)); - } - return mode; - } - private int getMountModeInternal(int uid, String packageName) { try { // Get some easy cases out of the way first @@ -4398,17 +4328,6 @@ class StorageManagerService extends IStorageManager.Stub pw.println(); pw.println("Local unlocked users: " + mLocalUnlockedUsers); pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); - - final ContentResolver cr = mContext.getContentResolver(); - pw.println(); - pw.println("Isolated storage, local feature flag: " - + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0)); - pw.println("Isolated storage, remote feature flag: " - + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0)); - pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage()); - pw.println("Forced scoped storage app list: " - + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, - PROP_FORCED_SCOPED_STORAGE_WHITELIST)); pw.println("isAutomotive:" + mIsAutomotive); } @@ -4460,20 +4379,10 @@ class StorageManagerService extends IStorageManager.Stub } private final class StorageManagerInternalImpl extends StorageManagerInternal { - // Not guarded by a lock. - private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies = - new CopyOnWriteArrayList<>(); - @GuardedBy("mResetListeners") private final List<StorageManagerInternal.ResetListener> mResetListeners = new ArrayList<>(); - @Override - public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { - // No locking - CopyOnWriteArrayList - mPolicies.add(policy); - } - /** * Check if fuse is running in target user, if it's running then setup its storage dirs. * Return true if storage dirs are mounted. @@ -4518,30 +4427,12 @@ class StorageManagerService extends IStorageManager.Stub @Override public int getExternalStorageMountMode(int uid, String packageName) { - if (ENABLE_ISOLATED_STORAGE) { - return getMountMode(uid, packageName); - } - try { - if (packageName == null) { - final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid); - packageName = packagesForUid[0]; - } - } catch (RemoteException e) { - // Should not happen - same process - } - // No locking - CopyOnWriteArrayList - int mountMode = Integer.MAX_VALUE; - for (ExternalStorageMountPolicy policy : mPolicies) { - final int policyMode = policy.getMountMode(uid, packageName); - if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - mountMode = Math.min(mountMode, policyMode); - } - if (mountMode == Integer.MAX_VALUE) { - return Zygote.MOUNT_EXTERNAL_NONE; + final int mode = getMountModeInternal(uid, packageName); + if (LOCAL_LOGV) { + Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/" + + UserHandle.formatUid(uid)); } - return mountMode; + return mode; } @Override @@ -4611,17 +4502,8 @@ class StorageManagerService extends IStorageManager.Stub if (uid == Process.SYSTEM_UID) { return true; } - if (ENABLE_ISOLATED_STORAGE) { - return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE; - } - // No locking - CopyOnWriteArrayList - for (ExternalStorageMountPolicy policy : mPolicies) { - final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); - if (!policyHasStorage) { - return false; - } - } - return true; + + return getExternalStorageMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE; } private void killAppForOpChange(int code, int uid) { diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java index 61a7d00faeb8..fcba9b56a541 100644 --- a/services/core/java/com/android/server/SystemUpdateManagerService.java +++ b/services/core/java/com/android/server/SystemUpdateManagerService.java @@ -201,7 +201,7 @@ public class SystemUpdateManagerService extends ISystemUpdateManager.Stub { // Performs I/O work only, without validating the loaded info. @Nullable - private PersistableBundle readInfoFileLocked(XmlPullParser parser) + private PersistableBundle readInfoFileLocked(TypedXmlPullParser parser) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != END_DOCUMENT) { diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING index ebeec39d6ae6..95af84293377 100644 --- a/services/core/java/com/android/server/TEST_MAPPING +++ b/services/core/java/com/android/server/TEST_MAPPING @@ -29,6 +29,10 @@ { "name": "CtsScopedStorageHostTest", "file_patterns": ["StorageManagerService\\.java"] + }, + { + "name": "CtsScopedStorageDeviceOnlyTest", + "file_patterns": ["StorageManagerService\\.java"] } ] } diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index d6bd5a1d7c4c..a45466d98563 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -29,9 +29,9 @@ import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkAgent; +import android.net.NetworkAgentConfig; import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.NetworkInfo.DetailedState; +import android.net.NetworkProvider; import android.net.RouteInfo; import android.net.StringNetworkSpecifier; import android.net.TestNetworkInterface; @@ -62,7 +62,8 @@ import java.util.concurrent.atomic.AtomicInteger; /** @hide */ class TestNetworkService extends ITestNetworkManager.Stub { @NonNull private static final String TAG = TestNetworkService.class.getSimpleName(); - @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK"; + @NonNull private static final String TEST_NETWORK_LOGTAG = "TestNetworkAgent"; + @NonNull private static final String TEST_NETWORK_PROVIDER_NAME = TAG; @NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger(); @NonNull private final Context mContext; @@ -72,6 +73,9 @@ class TestNetworkService extends ITestNetworkManager.Stub { @NonNull private final HandlerThread mHandlerThread; @NonNull private final Handler mHandler; + @NonNull private final ConnectivityManager mCm; + @NonNull private final NetworkProvider mNetworkProvider; + // Native method stubs private static native int jniCreateTunTap(boolean isTun, @NonNull String iface); @@ -85,6 +89,10 @@ class TestNetworkService extends ITestNetworkManager.Stub { mContext = Objects.requireNonNull(context, "missing Context"); mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance"); + mCm = mContext.getSystemService(ConnectivityManager.class); + mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(), + TEST_NETWORK_PROVIDER_NAME); + mCm.registerNetworkProvider(mNetworkProvider); } /** @@ -150,9 +158,6 @@ class TestNetworkService extends ITestNetworkManager.Stub { private static final int NETWORK_SCORE = 1; // Use a low, non-zero score. private final int mUid; - @NonNull private final NetworkInfo mNi; - @NonNull private final NetworkCapabilities mNc; - @NonNull private final LinkProperties mLp; @GuardedBy("mBinderLock") @NonNull @@ -161,20 +166,18 @@ class TestNetworkService extends ITestNetworkManager.Stub { @NonNull private final Object mBinderLock = new Object(); private TestNetworkAgent( - @NonNull Looper looper, @NonNull Context context, - @NonNull NetworkInfo ni, + @NonNull Looper looper, + @NonNull NetworkAgentConfig config, @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int uid, - @NonNull IBinder binder) + @NonNull IBinder binder, + @NonNull NetworkProvider np) throws RemoteException { - super(looper, context, TEST_NETWORK_TYPE, ni, nc, lp, NETWORK_SCORE); + super(context, looper, TEST_NETWORK_LOGTAG, nc, lp, NETWORK_SCORE, config, np); mUid = uid; - mNi = ni; - mNc = nc; - mLp = lp; synchronized (mBinderLock) { mBinder = binder; // Binder null-checks in create() @@ -203,9 +206,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { } private void teardown() { - mNi.setDetailedState(DetailedState.DISCONNECTED, null, null); - mNi.setIsAvailable(false); - sendNetworkInfo(mNi); + unregister(); // Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than // once (otherwise it could throw an exception) @@ -238,11 +239,6 @@ class TestNetworkService extends ITestNetworkManager.Stub { Objects.requireNonNull(context, "missing Context"); // iface and binder validity checked by caller - // Build network info with special testing type - NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_TEST, 0, TEST_NETWORK_TYPE, ""); - ni.setDetailedState(DetailedState.CONNECTED, null, null); - ni.setIsAvailable(true); - // Build narrow set of NetworkCapabilities, useful only for testing NetworkCapabilities nc = new NetworkCapabilities(); nc.clearAll(); // Remove default capabilities. @@ -290,7 +286,12 @@ class TestNetworkService extends ITestNetworkManager.Stub { lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface)); } - return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder); + final TestNetworkAgent agent = new TestNetworkAgent(context, looper, + new NetworkAgentConfig.Builder().build(), nc, lp, callingUid, binder, + mNetworkProvider); + agent.register(); + agent.markConnected(); + return agent; } /** diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java index 1c77a7f451b4..b379b5dc681a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java +++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java @@ -23,25 +23,21 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.PackageUtils; import android.util.Pair; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; + import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.accounts.AccountsDb.DeDatabaseHelper; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -163,7 +159,7 @@ public final class AccountManagerBackupHelper { } try { ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - final XmlSerializer serializer = new FastXmlSerializer(); + final TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(dataStream, StandardCharsets.UTF_8.name()); serializer.startDocument(null, true); serializer.startTag(null, TAG_PERMISSIONS); @@ -216,7 +212,7 @@ public final class AccountManagerBackupHelper { public void restoreAccountAccessPermissions(byte[] data, int userId) { try { ByteArrayInputStream dataStream = new ByteArrayInputStream(data); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(dataStream, StandardCharsets.UTF_8.name()); PackageManager packageManager = mAccountManagerService.mContext.getPackageManager(); diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java index f59af76ead8f..d99b195351f1 100644 --- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java +++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java @@ -1947,8 +1947,7 @@ public class AdbDebuggingManager { + tagName); return keyMap; } - int keystoreVersion = Integer.parseInt( - parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION)); + int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION); if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) { Slog.e(TAG, "Keystore version=" + keystoreVersion + " not supported (max_supported=" @@ -2068,8 +2067,7 @@ public class AdbDebuggingManager { + tagName); return trustedNetworks; } - int keystoreVersion = Integer.parseInt( - parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION)); + int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION); if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) { Slog.e(TAG, "Keystore version=" + keystoreVersion + " not supported (max_supported=" @@ -2148,7 +2146,7 @@ public class AdbDebuggingManager { serializer.startDocument(null, true); serializer.startTag(null, XML_KEYSTORE_START_TAG); - serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION)); + serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, KEYSTORE_VERSION); for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) { serializer.startTag(null, XML_TAG_ADB_KEY); serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey()); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 872902626fb4..c7e3b2344f54 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -91,6 +91,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.TransactionTooLargeException; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; @@ -165,6 +166,7 @@ public final class ActiveServices { public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18; @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { FGS_FEATURE_DENIED, @@ -183,7 +185,8 @@ public final class ActiveServices { FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, - FGS_FEATURE_ALLOWED_BY_FGS_BINDING + FGS_FEATURE_ALLOWED_BY_FGS_BINDING, + FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE }) @Retention(RetentionPolicy.SOURCE) public @interface FgsFeatureRetCode {} @@ -4987,14 +4990,16 @@ public final class ActiveServices { * - the first arg isn't the flattened component name of an existing service: * dump all services whose component contains the first arg as a substring */ - protected boolean dumpService(FileDescriptor fd, PrintWriter pw, final String name, + protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, int[] users, String[] args, int opti, boolean dumpAll) { final ArrayList<ServiceRecord> services = new ArrayList<>(); final Predicate<ServiceRecord> filter = DumpUtils.filterRecord(name); synchronized (mAm) { - int[] users = mAm.mUserController.getUsers(); + if (users == null) { + users = mAm.mUserController.getUsers(); + } for (int user : users) { ServiceMap smap = mServiceMap.get(user); @@ -5039,11 +5044,13 @@ public final class ActiveServices { String innerPrefix = prefix + " "; synchronized (mAm) { pw.print(prefix); pw.print("SERVICE "); - pw.print(r.shortInstanceName); pw.print(" "); - pw.print(Integer.toHexString(System.identityHashCode(r))); - pw.print(" pid="); - if (r.app != null) pw.println(r.app.pid); - else pw.println("(not running)"); + pw.print(r.shortInstanceName); pw.print(" "); + pw.print(Integer.toHexString(System.identityHashCode(r))); + pw.print(" pid="); + if (r.app != null) { + pw.print(r.app.pid); + pw.print(" user="); pw.println(r.userId); + } else pw.println("(not running)"); if (dumpAll) { r.dump(pw, innerPrefix); } @@ -5283,6 +5290,12 @@ public final class ActiveServices { } } + if (ret == FGS_FEATURE_DENIED) { + if (UserManager.isDeviceInDemoMode(mAm.mContext)) { + ret = FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE; + } + } + final String debugInfo = "[callingPackage: " + callingPackage + "; callingUid: " + callingUid @@ -5336,6 +5349,8 @@ public final class ActiveServices { return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: return "ALLOWED_BY_FGS_BINDING"; + case FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE: + return "ALLOWED_BY_DEVICE_DEMO_MODE"; default: return ""; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 75e8b13ccc0d..da6e7ff84525 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -252,6 +252,7 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; +import android.os.SharedMemory; import android.os.ShellCallback; import android.os.StrictMode; import android.os.SystemClock; @@ -348,6 +349,7 @@ import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; +import com.android.server.graphics.fonts.FontManagerInternal; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; import com.android.server.pm.permission.PermissionManagerServiceInternal; @@ -4358,6 +4360,11 @@ public class ActivityManagerService extends IActivityManager.Stub app.info.packageName); } } + SharedMemory serializedSystemFontMap = null; + final FontManagerInternal fm = LocalServices.getService(FontManagerInternal.class); + if (fm != null) { + serializedSystemFontMap = fm.getSerializedSystemFontMap(); + } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.elapsedRealtime(); @@ -4383,7 +4390,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, - app.mDisabledCompatChanges); + app.mDisabledCompatChanges, serializedSystemFontMap); } else { thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, @@ -4393,7 +4400,7 @@ public class ActivityManagerService extends IActivityManager.Stub app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), buildSerial, autofillOptions, contentCaptureOptions, - app.mDisabledCompatChanges); + app.mDisabledCompatChanges, serializedSystemFontMap); } if (profilerInfo != null) { profilerInfo.closeFd(); @@ -5644,7 +5651,7 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public List<RunningTaskInfo> getTasks(int maxNum) { - return mActivityTaskManager.getTasks(maxNum); + return mActivityTaskManager.getTasks(maxNum, false /* filterForVisibleRecents */); } @Override @@ -8664,17 +8671,30 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("service".equals(cmd)) { String[] newArgs; String name; + int[] users = null; if (opti >= args.length) { name = null; newArgs = EMPTY_STRING_ARRAY; } else { name = args[opti]; opti++; + if ("--user".equals(name) && opti < args.length) { + int userId = UserHandle.parseUserArg(args[opti]); + opti++; + if (userId != UserHandle.USER_ALL) { + if (userId == UserHandle.USER_CURRENT) { + userId = getCurrentUser().id; + } + users = new int[] { userId }; + } + name = args[opti]; + opti++; + } newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } - if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) { + if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); } diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 28afcbbb2a86..7299e814b020 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -88,6 +88,8 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, DeviceConfig.NAMESPACE_RUNTIME_NATIVE, DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT, + DeviceConfig.NAMESPACE_STATSD_NATIVE, + DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT, DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT, }; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index da4704097ad0..d4e2d27ca7a1 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -124,7 +124,6 @@ import android.os.ShellCommand; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; -import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.provider.Settings; import android.util.ArrayMap; @@ -155,10 +154,8 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IAppOpsStartedCallback; import com.android.internal.app.MessageSamplingConfig; import com.android.internal.compat.IPlatformCompat; -import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; @@ -175,7 +172,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -185,7 +181,6 @@ import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -1057,19 +1052,20 @@ public class AppOpsService extends IAppOpsService.Stub { } int numInProgressEvents = mInProgressEvents.size(); + List<IBinder> binders = new ArrayList<>(mInProgressEvents.keySet()); for (int i = 0; i < numInProgressEvents; i++) { - InProgressStartOpEvent event = mInProgressEvents.valueAt(i); + InProgressStartOpEvent event = mInProgressEvents.get(binders.get(i)); - if (event.getUidState() != newState) { + if (event != null && event.getUidState() != newState) { try { // Remove all but one unfinished start count and then call finished() to // remove start event object int numPreviousUnfinishedStarts = event.numUnfinishedStarts; event.numUnfinishedStarts = 1; - finished(event.getClientId(), false); - OpEventProxyInfo proxy = event.getProxy(); + finished(event.getClientId(), false); + // Call started() to add a new start event object and then add the // previously removed unfinished start counts back if (proxy != null) { @@ -1079,7 +1075,11 @@ public class AppOpsService extends IAppOpsService.Stub { started(event.getClientId(), Process.INVALID_UID, null, null, newState, OP_FLAG_SELF, false); } - event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1; + + InProgressStartOpEvent newEvent = mInProgressEvents.get(binders.get(i)); + if (newEvent != null) { + newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1; + } } catch (RemoteException e) { if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState); } @@ -1764,26 +1764,6 @@ public class AppOpsService extends IAppOpsService.Stub { } }); - if (!StorageManager.hasIsolatedStorage()) { - StorageManagerInternal storageManagerInternal = LocalServices.getService( - StorageManagerInternal.class); - storageManagerInternal.addExternalStoragePolicy( - new StorageManagerInternal.ExternalStorageMountPolicy() { - @Override - public int getMountMode(int uid, String packageName) { - if (Process.isIsolated(uid)) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } - - @Override - public boolean hasExternalStorage(int uid, String packageName) { - final int mountMode = getMountMode(uid, packageName); - return mountMode != Zygote.MOUNT_EXTERNAL_NONE; - } - }); - } mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); } @@ -4285,10 +4265,7 @@ public class AppOpsService extends IAppOpsService.Stub { throw new IllegalStateException("no start tag found"); } - final String versionString = parser.getAttributeValue(null, "v"); - if (versionString != null) { - oldVersion = Integer.parseInt(versionString); - } + oldVersion = parser.getAttributeInt(null, "v", NO_VERSION); int outerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4388,9 +4365,9 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleFastWriteLocked(); } - private void readUidOps(XmlPullParser parser) throws NumberFormatException, + private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { - final int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); + final int uid = parser.getAttributeInt(null, "n"); int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -4401,8 +4378,8 @@ public class AppOpsService extends IAppOpsService.Stub { String tagName = parser.getName(); if (tagName.equals("op")) { - final int code = Integer.parseInt(parser.getAttributeValue(null, "n")); - final int mode = Integer.parseInt(parser.getAttributeValue(null, "m")); + final int code = parser.getAttributeInt(null, "n"); + final int mode = parser.getAttributeInt(null, "m"); setUidMode(code, uid, mode); } else { Slog.w(TAG, "Unknown element under <uid-ops>: " @@ -4412,7 +4389,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readPackage(XmlPullParser parser) + private void readPackage(TypedXmlPullParser parser) throws NumberFormatException, XmlPullParserException, IOException { String pkgName = parser.getAttributeValue(null, "n"); int outerDepth = parser.getDepth(); @@ -4434,9 +4411,9 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readUid(XmlPullParser parser, String pkgName) + private void readUid(TypedXmlPullParser parser, String pkgName) throws NumberFormatException, XmlPullParserException, IOException { - int uid = Integer.parseInt(parser.getAttributeValue(null, "n")); + int uid = parser.getAttributeInt(null, "n"); final UidState uidState = getUidStateLocked(uid, true); int outerDepth = parser.getDepth(); int type; @@ -4457,19 +4434,20 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.evalForegroundOps(mOpModeWatchers); } - private void readAttributionOp(XmlPullParser parser, @NonNull Op parent, - @Nullable String attribution) throws NumberFormatException, IOException { + private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent, + @Nullable String attribution) + throws NumberFormatException, IOException, XmlPullParserException { final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution); - final long key = XmlUtils.readLongAttribute(parser, "n"); + final long key = parser.getAttributeLong(null, "n"); final int uidState = extractUidStateFromKey(key); final int opFlags = extractFlagsFromKey(key); - final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0); - final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0); - final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1); + final long accessTime = parser.getAttributeLong(null, "t", 0); + final long rejectTime = parser.getAttributeLong(null, "r", 0); + final long accessDuration = parser.getAttributeLong(null, "d", -1); final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp"); - final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID); + final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID); final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc"); if (accessTime > 0) { @@ -4481,14 +4459,13 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName) - throws NumberFormatException, - XmlPullParserException, IOException { - int opCode = Integer.parseInt(parser.getAttributeValue(null, "n")); + private void readOp(TypedXmlPullParser parser, + @NonNull UidState uidState, @NonNull String pkgName) + throws NumberFormatException, XmlPullParserException, IOException { + int opCode = parser.getAttributeInt(null, "n"); Op op = new Op(uidState, pkgName, opCode, uidState.uid); - final int mode = XmlUtils.readIntAttribute(parser, "m", - AppOpsManager.opToDefaultMode(op.op)); + final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op)); op.mode = mode; int outerDepth = parser.getDepth(); @@ -4535,7 +4512,7 @@ public class AppOpsService extends IAppOpsService.Stub { TypedXmlSerializer out = Xml.resolveSerializer(stream); out.startDocument(null, true); out.startTag(null, "app-ops"); - out.attribute(null, "v", String.valueOf(CURRENT_VERSION)); + out.attributeInt(null, "v", CURRENT_VERSION); SparseArray<SparseIntArray> uidStatesClone; synchronized (this) { @@ -4565,15 +4542,14 @@ public class AppOpsService extends IAppOpsService.Stub { SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum); if (opModes != null && opModes.size() > 0) { out.startTag(null, "uid"); - out.attribute(null, "n", - Integer.toString(uidStatesClone.keyAt(uidStateNum))); + out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum)); final int opCount = opModes.size(); for (int opCountNum = 0; opCountNum < opCount; opCountNum++) { final int op = opModes.keyAt(opCountNum); final int mode = opModes.valueAt(opCountNum); out.startTag(null, "op"); - out.attribute(null, "n", Integer.toString(op)); - out.attribute(null, "m", Integer.toString(mode)); + out.attributeInt(null, "n", op); + out.attributeInt(null, "m", mode); out.endTag(null, "op"); } out.endTag(null, "uid"); @@ -4593,14 +4569,14 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "n", lastPkg); } out.startTag(null, "uid"); - out.attribute(null, "n", Integer.toString(pkg.getUid())); + out.attributeInt(null, "n", pkg.getUid()); List<AppOpsManager.OpEntry> ops = pkg.getOps(); for (int j=0; j<ops.size(); j++) { AppOpsManager.OpEntry op = ops.get(j); out.startTag(null, "op"); - out.attribute(null, "n", Integer.toString(op.getOp())); + out.attributeInt(null, "n", op.getOp()); if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) { - out.attribute(null, "m", Integer.toString(op.getMode())); + out.attributeInt(null, "m", op.getMode()); } for (String attributionTag : op.getAttributedOpEntries().keySet()) { @@ -4644,15 +4620,15 @@ public class AppOpsService extends IAppOpsService.Stub { if (attributionTag != null) { out.attribute(null, "id", attributionTag); } - out.attribute(null, "n", Long.toString(key)); + out.attributeLong(null, "n", key); if (accessTime > 0) { - out.attribute(null, "t", Long.toString(accessTime)); + out.attributeLong(null, "t", accessTime); } if (rejectTime > 0) { - out.attribute(null, "r", Long.toString(rejectTime)); + out.attributeLong(null, "r", rejectTime); } if (accessDuration > 0) { - out.attribute(null, "d", Long.toString(accessDuration)); + out.attributeLong(null, "d", accessDuration); } if (proxyPkg != null) { out.attribute(null, "pp", proxyPkg); @@ -4661,7 +4637,7 @@ public class AppOpsService extends IAppOpsService.Stub { out.attribute(null, "pc", proxyAttributionTag); } if (proxyUid >= 0) { - out.attribute(null, "pu", Integer.toString(proxyUid)); + out.attributeInt(null, "pu", proxyUid); } out.endTag(null, "st"); } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index f49b5dca2b08..676fcd0bdc87 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -49,6 +49,8 @@ import android.util.ArraySet; import android.util.LongSparseArray; import android.util.Slog; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -1184,19 +1186,18 @@ final class HistoricalRegistry { } List<HistoricalOps> allOps = null; try (FileInputStream stream = new FileInputStream(file)) { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlPullParser parser = Xml.resolvePullParser(stream); XmlUtils.beginDocument(parser, TAG_HISTORY); // We haven't released version 1 and have more detailed // accounting - just nuke the current state - final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); + final int version = parser.getAttributeInt(null, ATTR_VERSION); if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) { throw new IllegalStateException("Dropping unsupported history " + "version 1 for file:" + file); } - final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0); + final long overflowMillis = parser.getAttributeLong(null, ATTR_OVERFLOW, 0); final int depth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, depth)) { if (TAG_OPS.equals(parser.getName())) { @@ -1235,15 +1236,16 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readeHistoricalOpsDLocked( - @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName, + @NonNull TypedXmlPullParser parser, int filterUid, + @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags, @Nullable long[] cumulativeOverflowMillis) throws IOException, XmlPullParserException { - final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0) + final long beginTimeMillis = parser.getAttributeLong(null, ATTR_BEGIN_TIME, 0) + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0); - final long endTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_END_TIME, 0) + final long endTimeMillis = parser.getAttributeLong(null, ATTR_END_TIME, 0) + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0); // Keep reading as subsequent records may start matching if (filterEndTimeMillis < beginTimeMillis) { @@ -1280,12 +1282,12 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readHistoricalUidOpsDLocked( - @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid, + @Nullable HistoricalOps ops, @NonNull TypedXmlPullParser parser, int filterUid, @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { - final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME); + final int uid = parser.getAttributeInt(null, ATTR_NAME); if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) { XmlUtils.skipCurrentTag(parser); return null; @@ -1305,7 +1307,7 @@ final class HistoricalRegistry { } private @Nullable HistoricalOps readHistoricalPackageOpsDLocked( - @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser, + @Nullable HistoricalOps ops, int uid, @NonNull TypedXmlPullParser parser, @Nullable String filterPackageName, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) @@ -1331,7 +1333,7 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked( @Nullable HistoricalOps ops, int uid, String packageName, - @NonNull XmlPullParser parser, @Nullable String filterAttributionTag, + @NonNull TypedXmlPullParser parser, @Nullable String filterAttributionTag, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { @@ -1357,11 +1359,11 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops, int uid, @NonNull String packageName, @Nullable String attributionTag, - @NonNull XmlPullParser parser, @Nullable String[] filterOpNames, + @NonNull TypedXmlPullParser parser, @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags, double filterScale) throws IOException, XmlPullParserException { - final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME); + final int op = parser.getAttributeInt(null, ATTR_NAME); if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames, AppOpsManager.opToPublicName(op))) { XmlUtils.skipCurrentTag(parser); @@ -1382,15 +1384,15 @@ final class HistoricalRegistry { private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops, int uid, @NonNull String packageName, @Nullable String attributionTag, int op, - @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale) - throws IOException { - final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME); + @NonNull TypedXmlPullParser parser, @OpFlags int filterFlags, double filterScale) + throws IOException, XmlPullParserException { + final long key = parser.getAttributeLong(null, ATTR_NAME); final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags; if (flags == 0) { return null; } final int uidState = AppOpsManager.extractUidStateFromKey(key); - long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0); + long accessCount = parser.getAttributeLong(null, ATTR_ACCESS_COUNT, 0); if (accessCount > 0) { if (!Double.isNaN(filterScale)) { accessCount = (long) HistoricalOps.round( @@ -1402,7 +1404,7 @@ final class HistoricalRegistry { ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags, accessCount); } - long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0); + long rejectCount = parser.getAttributeLong(null, ATTR_REJECT_COUNT, 0); if (rejectCount > 0) { if (!Double.isNaN(filterScale)) { rejectCount = (long) HistoricalOps.round( @@ -1414,7 +1416,7 @@ final class HistoricalRegistry { ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags, rejectCount); } - long accessDuration = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0); + long accessDuration = parser.getAttributeLong(null, ATTR_ACCESS_DURATION, 0); if (accessDuration > 0) { if (!Double.isNaN(filterScale)) { accessDuration = (long) HistoricalOps.round( @@ -1433,16 +1435,14 @@ final class HistoricalRegistry { long intervalOverflowMillis, @NonNull File file) throws IOException { final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file); try { - final XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(output, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer serializer = Xml.resolveSerializer(output); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_HISTORY); - serializer.attribute(null, ATTR_VERSION, String.valueOf(CURRENT_VERSION)); + serializer.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); if (intervalOverflowMillis != 0) { - serializer.attribute(null, ATTR_OVERFLOW, - Long.toString(intervalOverflowMillis)); + serializer.attributeLong(null, ATTR_OVERFLOW, intervalOverflowMillis); } if (allOps != null) { final int opsCount = allOps.size(); @@ -1461,10 +1461,10 @@ final class HistoricalRegistry { } private void writeHistoricalOpDLocked(@NonNull HistoricalOps ops, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_OPS); - serializer.attribute(null, ATTR_BEGIN_TIME, Long.toString(ops.getBeginTimeMillis())); - serializer.attribute(null, ATTR_END_TIME, Long.toString(ops.getEndTimeMillis())); + serializer.attributeLong(null, ATTR_BEGIN_TIME, ops.getBeginTimeMillis()); + serializer.attributeLong(null, ATTR_END_TIME, ops.getEndTimeMillis()); final int uidCount = ops.getUidCount(); for (int i = 0; i < uidCount; i++) { final HistoricalUidOps uidOp = ops.getUidOpsAt(i); @@ -1474,9 +1474,9 @@ final class HistoricalRegistry { } private void writeHistoricalUidOpsDLocked(@NonNull HistoricalUidOps uidOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_UID); - serializer.attribute(null, ATTR_NAME, Integer.toString(uidOps.getUid())); + serializer.attributeInt(null, ATTR_NAME, uidOps.getUid()); final int packageCount = uidOps.getPackageCount(); for (int i = 0; i < packageCount; i++) { final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(i); @@ -1486,7 +1486,7 @@ final class HistoricalRegistry { } private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_PACKAGE); serializer.attribute(null, ATTR_NAME, packageOps.getPackageName()); final int numAttributions = packageOps.getAttributedOpsCount(); @@ -1499,7 +1499,7 @@ final class HistoricalRegistry { private void writeHistoricalAttributionOpsDLocked( @NonNull AppOpsManager.AttributedHistoricalOps attributionOps, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_ATTRIBUTION); XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag()); final int opCount = attributionOps.getOpCount(); @@ -1511,13 +1511,13 @@ final class HistoricalRegistry { } private void writeHistoricalOpDLocked(@NonNull HistoricalOp op, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { final LongSparseArray keys = op.collectKeys(); if (keys == null || keys.size() <= 0) { return; } serializer.startTag(null, TAG_OP); - serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode())); + serializer.attributeInt(null, ATTR_NAME, op.getOpCode()); final int keyCount = keys.size(); for (int i = 0; i < keyCount; i++) { writeStateOnLocked(op, keys.keyAt(i), serializer); @@ -1526,7 +1526,7 @@ final class HistoricalRegistry { } private void writeStateOnLocked(@NonNull HistoricalOp op, long key, - @NonNull XmlSerializer serializer) throws IOException { + @NonNull TypedXmlSerializer serializer) throws IOException { final int uidState = AppOpsManager.extractUidStateFromKey(key); final int flags = AppOpsManager.extractFlagsFromKey(key); @@ -1539,15 +1539,15 @@ final class HistoricalRegistry { } serializer.startTag(null, TAG_STATE); - serializer.attribute(null, ATTR_NAME, Long.toString(key)); + serializer.attributeLong(null, ATTR_NAME, key); if (accessCount > 0) { - serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount)); + serializer.attributeLong(null, ATTR_ACCESS_COUNT, accessCount); } if (rejectCount > 0) { - serializer.attribute(null, ATTR_REJECT_COUNT, Long.toString(rejectCount)); + serializer.attributeLong(null, ATTR_REJECT_COUNT, rejectCount); } if (accessDuration > 0) { - serializer.attribute(null, ATTR_ACCESS_DURATION, Long.toString(accessDuration)); + serializer.attributeLong(null, ATTR_ACCESS_DURATION, accessDuration); } serializer.endTag(null, TAG_STATE); } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index ff410fcd2f66..0943c9ca17fe 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -49,7 +49,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private final boolean mIsStrongBiometric; private final boolean mRequireConfirmation; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @Nullable private final TaskStackListener mTaskStackListener; private final LockoutTracker mLockoutTracker; private final boolean mIsRestricted; @@ -71,7 +71,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = taskStackListener; mLockoutTracker = lockoutTracker; mIsRestricted = restricted; @@ -146,29 +146,24 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> // Ensure authentication only succeeds if the client activity is on top or is keyguard. boolean isBackgroundAuth = false; if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) { - try { - final List<ActivityManager.RunningTaskInfo> tasks = - mActivityTaskManager.getTasks(1); - if (tasks == null || tasks.isEmpty()) { - Slog.e(TAG, "No running tasks reported"); + final List<ActivityManager.RunningTaskInfo> tasks = + mActivityTaskManager.getTasks(1); + if (tasks == null || tasks.isEmpty()) { + Slog.e(TAG, "No running tasks reported"); + isBackgroundAuth = true; + } else { + final ComponentName topActivity = tasks.get(0).topActivity; + if (topActivity == null) { + Slog.e(TAG, "Unable to get top activity"); isBackgroundAuth = true; } else { - final ComponentName topActivity = tasks.get(0).topActivity; - if (topActivity == null) { - Slog.e(TAG, "Unable to get top activity"); + final String topPackage = topActivity.getPackageName(); + if (!topPackage.contentEquals(getOwnerString())) { + Slog.e(TAG, "Background authentication detected, top: " + topPackage + + ", client: " + this); isBackgroundAuth = true; - } else { - final String topPackage = topActivity.getPackageName(); - if (!topPackage.contentEquals(getOwnerString())) { - Slog.e(TAG, "Background authentication detected, top: " + topPackage - + ", client: " + this); - isBackgroundAuth = true; - } } } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get running tasks", e); - isBackgroundAuth = true; } } @@ -198,11 +193,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } if (mTaskStackListener != null) { - try { - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not unregister task stack listener", e); - } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); } final byte[] byteToken = new byte[hardwareAuthToken.size()]; @@ -290,11 +281,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } if (mTaskStackListener != null) { - try { - mActivityTaskManager.registerTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not register task stack listener", e); - } + mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString()); @@ -309,11 +296,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> super.cancel(); if (mTaskStackListener != null) { - try { - mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); - } catch (RemoteException e) { - Slog.e(TAG, "Could not unregister task stack listener", e); - } + mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java index a8250ac9e72d..d588b8d4aa13 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java @@ -22,6 +22,7 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.os.AsyncTask; import android.os.Environment; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -80,7 +81,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi /** * @return */ - protected abstract void parseBiometricsLocked(XmlPullParser parser) + protected abstract void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException; @@ -176,8 +177,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi return; } try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parseStateLocked(parser); } catch (XmlPullParserException | IOException e) { @@ -189,7 +189,7 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi } @GuardedBy("this") - private void parseStateLocked(XmlPullParser parser) + private void parseStateLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java index d30c3c804362..78e875b864f4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java @@ -20,6 +20,8 @@ import android.content.Context; import android.hardware.face.Face; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -29,7 +31,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.FileOutputStream; import java.io.IOException; @@ -87,8 +88,7 @@ public class FaceUserState extends BiometricUserState<Face> { try { out = destination.startWrite(); - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(out, "utf-8"); + TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_FACES); @@ -97,9 +97,9 @@ public class FaceUserState extends BiometricUserState<Face> { for (int i = 0; i < count; i++) { Face f = faces.get(i); serializer.startTag(null, TAG_FACE); - serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getBiometricId())); + serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId()); serializer.attribute(null, ATTR_NAME, f.getName().toString()); - serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId())); + serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId()); serializer.endTag(null, TAG_FACE); } @@ -119,7 +119,7 @@ public class FaceUserState extends BiometricUserState<Face> { @GuardedBy("this") @Override - protected void parseBiometricsLocked(XmlPullParser parser) + protected void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); int type; @@ -132,9 +132,9 @@ public class FaceUserState extends BiometricUserState<Face> { String tagName = parser.getName(); if (tagName.equals(TAG_FACE)) { String name = parser.getAttributeValue(null, ATTR_NAME); - String faceId = parser.getAttributeValue(null, ATTR_FACE_ID); - String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID); - mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId))); + int faceId = parser.getAttributeInt(null, ATTR_FACE_ID); + int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID); + mBiometrics.add(new Face(name, faceId, deviceId)); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index cc9298603a3e..cec1cb8654fc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -77,7 +77,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final UsageStats mUsageStats; - @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; @Nullable private IFace mDaemon; @@ -97,22 +97,18 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { continue; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = - runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(getTag(), "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.valueAt(i).getScheduler() + .cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to get running tasks", e); } } }); @@ -129,7 +125,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler = new Handler(Looper.getMainLooper()); mUsageStats = new UsageStats(context); mLockoutResetDispatcher = lockoutResetDispatcher; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java index e56c8d5b5ceb..671e08bd8c56 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java @@ -20,6 +20,8 @@ import android.content.Context; import android.hardware.fingerprint.Fingerprint; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -29,7 +31,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.FileOutputStream; import java.io.IOException; @@ -88,8 +89,7 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { try { out = destination.startWrite(); - XmlSerializer serializer = Xml.newSerializer(); - serializer.setOutput(out, "utf-8"); + TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startTag(null, TAG_FINGERPRINTS); @@ -98,10 +98,10 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { for (int i = 0; i < count; i++) { Fingerprint fp = fingerprints.get(i); serializer.startTag(null, TAG_FINGERPRINT); - serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId())); + serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId()); serializer.attribute(null, ATTR_NAME, fp.getName().toString()); - serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId())); - serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId())); + serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId()); + serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId()); serializer.endTag(null, TAG_FINGERPRINT); } @@ -121,7 +121,7 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { @GuardedBy("this") @Override - protected void parseBiometricsLocked(XmlPullParser parser) + protected void parseBiometricsLocked(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); @@ -135,11 +135,10 @@ public class FingerprintUserState extends BiometricUserState<Fingerprint> { String tagName = parser.getName(); if (tagName.equals(TAG_FINGERPRINT)) { String name = parser.getAttributeValue(null, ATTR_NAME); - String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID); - String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID); - String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID); - mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId), - Integer.parseInt(fingerId), Long.parseLong(deviceId))); + int groupId = parser.getAttributeInt(null, ATTR_GROUP_ID); + int fingerId = parser.getAttributeInt(null, ATTR_FINGER_ID); + long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID); + mBiometrics.add(new Fingerprint(name, groupId, fingerId, deviceId)); } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 98c32cbcfc4a..99c662a57c3b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -77,7 +77,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; - @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final ActivityTaskManager mActivityTaskManager; @NonNull private final BiometricTaskStackListener mTaskStackListener; @Nullable private IFingerprint mDaemon; @@ -98,22 +98,18 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi continue; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = - runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(getTag(), "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mSensors.valueAt(i).getScheduler() - .cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.valueAt(i).getScheduler() + .cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to get running tasks", e); } } }); @@ -129,7 +125,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mLazyDaemon = this::getHalInstance; mHandler = new Handler(Looper.getMainLooper()); mLockoutResetDispatcher = lockoutResetDispatcher; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index f38dd092007a..7c5b7c92c1c6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -97,7 +97,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider private boolean mTestHalEnabled; final Context mContext; - private final IActivityTaskManager mActivityTaskManager; + private final ActivityTaskManager mActivityTaskManager; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; private final BiometricScheduler mScheduler; private final Handler mHandler; @@ -125,20 +125,16 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider return; // Keyguard is always allowed } - try { - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(client.getOwnerString()) - && !client.isAlreadyDone()) { - Slog.e(TAG, "Stopping background authentication, top: " - + topPackage + " currentClient: " + client); - mScheduler.cancelAuthentication(client.getToken()); - } + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(TAG, "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mScheduler.cancelAuthentication(client.getToken()); } - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get running tasks", e); } }); } @@ -313,7 +309,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mContext = context; mScheduler = scheduler; mHandler = handler; - mActivityTaskManager = ActivityTaskManager.getService(); + mActivityTaskManager = ActivityTaskManager.getInstance(); mTaskStackListener = new BiometricTaskStackListener(); mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>()); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index eec286a99a8f..3445275b76dd 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -18,10 +18,7 @@ package com.android.server.connectivity; import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.net.ConnectivityManager.NETID_UNSET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; @@ -111,7 +108,6 @@ import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnProfile; -import com.android.internal.util.ArrayUtils; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.net.BaseNetworkObserver; @@ -122,7 +118,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigInteger; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -152,36 +147,13 @@ import java.util.concurrent.atomic.AtomicInteger; public class Vpn { private static final String NETWORKTYPE = "VPN"; private static final String TAG = "Vpn"; + private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:"; private static final boolean LOGD = true; // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on // the device idle allowlist during service launch and VPN bootstrap. private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000; - // Settings for how much of the address space should be routed so that Vpn considers - // "most" of the address space is routed. This is used to determine whether this Vpn - // should be marked with the INTERNET capability. - private static final long MOST_IPV4_ADDRESSES_COUNT; - private static final BigInteger MOST_IPV6_ADDRESSES_COUNT; - static { - // 85% of the address space must be routed for Vpn to consider this VPN to provide - // INTERNET access. - final int howManyPercentIsMost = 85; - - final long twoPower32 = 1L << 32; - MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100; - final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128); - MOST_IPV6_ADDRESSES_COUNT = twoPower128 - .multiply(BigInteger.valueOf(howManyPercentIsMost)) - .divide(BigInteger.valueOf(100)); - } - // How many routes to evaluate before bailing and declaring this Vpn should provide - // the INTERNET capability. This is necessary because computing the address space is - // O(n²) and this is running in the system service, so a limit is needed to alleviate - // the risk of attack. - // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm - // is actually O(n²)+O(n²). - private static final int MAX_ROUTES_TO_EVALUATE = 150; private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME = Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST; /** @@ -198,6 +170,7 @@ public class Vpn { // automated reconnection private final Context mContext; + private final ConnectivityManager mConnectivityManager; // The context is for specific user which is created from mUserId private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; @@ -218,6 +191,7 @@ public class Vpn { private final INetworkManagementService mNetd; @VisibleForTesting protected VpnConfig mConfig; + private final NetworkProvider mNetworkProvider; @VisibleForTesting protected NetworkAgent mNetworkAgent; private final Looper mLooper; @@ -401,6 +375,7 @@ public class Vpn { int userId, @NonNull KeyStore keyStore, SystemServices systemServices, Ikev2SessionCreator ikev2SessionCreator) { mContext = context; + mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); mDeps = deps; mNetd = netService; @@ -419,13 +394,16 @@ public class Vpn { Log.wtf(TAG, "Problem registering observer", e); } + mNetworkProvider = new NetworkProvider(context, looper, VPN_PROVIDER_NAME_BASE + mUserId); + // This constructor is called in onUserStart and registers the provider. The provider + // will be unregistered in onUserStop. + mConnectivityManager.registerNetworkProvider(mNetworkProvider); mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED; mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE, "" /* subtypeName */); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN); mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN); - updateCapabilities(null /* defaultNetwork */); loadAlwaysOnPackage(keyStore); } @@ -443,110 +421,44 @@ public class Vpn { * Update current state, dispatching event to listeners. */ @VisibleForTesting + @GuardedBy("this") protected void updateState(DetailedState detailedState, String reason) { if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason); mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState); mNetworkInfo.setDetailedState(detailedState, reason, null); - if (mNetworkAgent != null) { - mNetworkAgent.sendNetworkInfo(mNetworkInfo); + // TODO : only accept transitions when the agent is in the correct state (non-null for + // CONNECTED, DISCONNECTED and FAILED, null for CONNECTED). + // This will require a way for tests to pretend the VPN is connected that's not + // calling this method with CONNECTED. + // It will also require audit of where the code calls this method with DISCONNECTED + // with a null agent, which it was doing historically to make sure the agent is + // disconnected as this was a no-op if the agent was null. + switch (detailedState) { + case CONNECTED: + if (null != mNetworkAgent) { + mNetworkAgent.markConnected(); + } + break; + case DISCONNECTED: + case FAILED: + if (null != mNetworkAgent) { + mNetworkAgent.unregister(); + mNetworkAgent = null; + } + break; + case CONNECTING: + if (null != mNetworkAgent) { + throw new IllegalStateException("VPN can only go to CONNECTING state when" + + " the agent is null."); + } + break; + default: + throw new IllegalArgumentException("Illegal state argument " + detailedState); } updateAlwaysOnNotification(detailedState); } /** - * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a - * defensive copy. - * - * <p>Does not propagate updated capabilities to apps. - * - * @param defaultNetwork underlying network for VPNs following platform's default - */ - public synchronized NetworkCapabilities updateCapabilities(@Nullable Network defaultNetwork) { - if (mConfig == null) { - // VPN is not running. - return null; - } - - Network[] underlyingNetworks = mConfig.underlyingNetworks; - if (underlyingNetworks == null && defaultNetwork != null) { - // null underlying networks means to track the default. - underlyingNetworks = new Network[] { defaultNetwork }; - } - // Only apps targeting Q and above can explicitly declare themselves as metered. - final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered; - - applyUnderlyingCapabilities( - mContext.getSystemService(ConnectivityManager.class), - underlyingNetworks, - mNetworkCapabilities, - isAlwaysMetered); - - return new NetworkCapabilities(mNetworkCapabilities); - } - - @VisibleForTesting - public static void applyUnderlyingCapabilities( - ConnectivityManager cm, - Network[] underlyingNetworks, - NetworkCapabilities caps, - boolean isAlwaysMetered) { - int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN }; - int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; - int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; - boolean metered = isAlwaysMetered; // metered if any underlying is metered, or alwaysMetered - boolean roaming = false; // roaming if any underlying is roaming - boolean congested = false; // congested if any underlying is congested - boolean suspended = true; // suspended if all underlying are suspended - - boolean hadUnderlyingNetworks = false; - if (null != underlyingNetworks) { - for (Network underlying : underlyingNetworks) { - // TODO(b/124469351): Get capabilities directly from ConnectivityService instead. - final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); - if (underlyingCaps == null) continue; - hadUnderlyingNetworks = true; - for (int underlyingType : underlyingCaps.getTransportTypes()) { - transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); - } - - // Merge capabilities of this underlying network. For bandwidth, assume the - // worst case. - downKbps = NetworkCapabilities.minBandwidth(downKbps, - underlyingCaps.getLinkDownstreamBandwidthKbps()); - upKbps = NetworkCapabilities.minBandwidth(upKbps, - underlyingCaps.getLinkUpstreamBandwidthKbps()); - // If this underlying network is metered, the VPN is metered (it may cost money - // to send packets on this network). - metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED); - // If this underlying network is roaming, the VPN is roaming (the billing structure - // is different than the usual, local one). - roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING); - // If this underlying network is congested, the VPN is congested (the current - // condition of the network affects the performance of this network). - congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); - // If this network is not suspended, the VPN is not suspended (the VPN - // is able to transfer some data). - suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED); - } - } - if (!hadUnderlyingNetworks) { - // No idea what the underlying networks are; assume the safer defaults - metered = true; - roaming = false; - congested = false; - suspended = false; - } - - caps.setTransportTypes(transportTypes); - caps.setLinkDownstreamBandwidthKbps(downKbps); - caps.setLinkUpstreamBandwidthKbps(upKbps); - caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered); - caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming); - caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested); - caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended); - } - - /** * Chooses whether to force all connections to go though VPN. * * Used to enable/disable legacy VPN lockdown. @@ -1016,7 +928,7 @@ public class Vpn { } mConfig = null; - updateState(DetailedState.IDLE, "prepare"); + updateState(DetailedState.DISCONNECTED, "prepare"); setVpnForcedLocked(mLockdown); } finally { Binder.restoreCallingIdentity(token); @@ -1252,7 +1164,7 @@ public class Vpn { mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mLegacyState = LegacyVpnInfo.STATE_CONNECTING; - mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); + updateState(DetailedState.CONNECTING, "agentConnect"); NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; @@ -1270,20 +1182,23 @@ public class Vpn { mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); } - final long token = Binder.clearCallingIdentity(); - try { - mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */, - mNetworkInfo, mNetworkCapabilities, lp, - ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, - NetworkProvider.ID_VPN) { - @Override - public void unwanted() { - // We are user controlled, not driven by NetworkRequest. - } - }; - } finally { - Binder.restoreCallingIdentity(token); - } + mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */, + mNetworkCapabilities, lp, + ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) { + @Override + public void unwanted() { + // We are user controlled, not driven by NetworkRequest. + } + }; + Binder.withCleanCallingIdentity(() -> { + try { + mNetworkAgent.register(); + } catch (final Exception e) { + // If register() throws, don't keep an unregistered agent. + mNetworkAgent = null; + throw e; + } + }); mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null) ? Arrays.asList(mConfig.underlyingNetworks) : null); mNetworkInfo.setIsAvailable(true); @@ -1301,19 +1216,12 @@ public class Vpn { private void agentDisconnect(NetworkAgent networkAgent) { if (networkAgent != null) { - NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo); - networkInfo.setIsAvailable(false); - networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); - networkAgent.sendNetworkInfo(networkInfo); + networkAgent.unregister(); } } private void agentDisconnect() { - if (mNetworkInfo.isConnected()) { - mNetworkInfo.setIsAvailable(false); - updateState(DetailedState.DISCONNECTED, "agentDisconnect"); - mNetworkAgent = null; - } + updateState(DetailedState.DISCONNECTED, "agentDisconnect"); } /** @@ -1402,6 +1310,8 @@ public class Vpn { && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) { // Keep mNetworkAgent unchanged } else { + // Initialize the state for a new agent, while keeping the old one connected + // in case this new connection fails. mNetworkAgent = null; updateState(DetailedState.CONNECTING, "establish"); // Set up forwarding and DNS rules. @@ -1585,12 +1495,13 @@ public class Vpn { try { addUserToRanges(existingRanges, userId, mConfig.allowedApplications, mConfig.disallowedApplications); - // ConnectivityService will call {@link #updateCapabilities} and apply - // those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1613,12 +1524,13 @@ public class Vpn { final List<UidRange> removedRanges = uidRangesForUser(userId, existingRanges); existingRanges.removeAll(removedRanges); - // ConnectivityService will call {@link #updateCapabilities} and - // apply those for VPN network. mNetworkCapabilities.setUids(existingRanges); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } } setVpnForcedLocked(mLockdown); } @@ -1635,6 +1547,9 @@ public class Vpn { // Quit any active connections agentDisconnect(); + + // The provider has been registered in the constructor, which is called in onUserStart. + mConnectivityManager.unregisterNetworkProvider(mNetworkProvider); } /** @@ -2411,7 +2326,6 @@ public class Vpn { // When restricted to test networks, select any network with TRANSPORT_TEST. Since the // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS, // this is considered safe. - final ConnectivityManager cm = ConnectivityManager.from(mContext); final NetworkRequest req; if (mProfile.isRestrictedToTestNetworks()) { @@ -2430,7 +2344,7 @@ public class Vpn { .build(); } - cm.requestNetwork(req, mNetworkCallback); + mConnectivityManager.requestNetwork(req, mNetworkCallback); } private boolean isActiveNetwork(@Nullable Network network) { @@ -2717,8 +2631,7 @@ public class Vpn { resetIkeState(); - final ConnectivityManager cm = ConnectivityManager.from(mContext); - cm.unregisterNetworkCallback(mNetworkCallback); + mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mExecutor.shutdown(); } @@ -2799,13 +2712,12 @@ public class Vpn { mProfile = profile; if (!TextUtils.isEmpty(mOuterInterface)) { - final ConnectivityManager cm = ConnectivityManager.from(mContext); - for (Network network : cm.getAllNetworks()) { - final LinkProperties lp = cm.getLinkProperties(network); + for (Network network : mConnectivityManager.getAllNetworks()) { + final LinkProperties lp = mConnectivityManager.getLinkProperties(network); if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) { - final NetworkInfo networkInfo = cm.getNetworkInfo(network); - if (networkInfo != null) { - mOuterConnection.set(networkInfo.getType()); + final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network); + if (netInfo != null) { + mOuterConnection.set(netInfo.getType()); break; } } diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java index 9a1f1e522f97..d27cb16ecc51 100644 --- a/services/core/java/com/android/server/content/SyncStorageEngine.java +++ b/services/core/java/com/android/server/content/SyncStorageEngine.java @@ -58,12 +58,10 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IntPair; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -71,7 +69,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -1650,37 +1647,23 @@ public class SyncStorageEngine { String tagName = parser.getName(); if ("accounts".equals(tagName)) { - String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES); - String versionString = parser.getAttributeValue(null, "version"); - int version; - try { - version = (versionString == null) ? 0 : Integer.parseInt(versionString); - } catch (NumberFormatException e) { - version = 0; - } + boolean listen = parser.getAttributeBoolean( + null, XML_ATTR_LISTEN_FOR_TICKLES, true); + int version = parser.getAttributeInt(null, "version", 0); if (version < 3) { mGrantSyncAdaptersAccountAccess = true; } - String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID); - try { - int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString); - mNextAuthorityId = Math.max(mNextAuthorityId, id); - } catch (NumberFormatException e) { - // don't care - } - String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET); - try { - mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString); - } catch (NumberFormatException e) { - mSyncRandomOffset = 0; - } + int nextId = parser.getAttributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, 0); + mNextAuthorityId = Math.max(mNextAuthorityId, nextId); + + mSyncRandomOffset = parser.getAttributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, 0); if (mSyncRandomOffset == 0) { Random random = new Random(System.currentTimeMillis()); mSyncRandomOffset = random.nextInt(86400); } - mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen)); + mMasterSyncAutomatically.put(0, listen); eventType = parser.next(); AuthorityInfo authority = null; PeriodicSync periodicSync = null; @@ -1804,27 +1787,23 @@ public class SyncStorageEngine { return writeNeeded; } - private void parseListenForTickles(XmlPullParser parser) { - String user = parser.getAttributeValue(null, XML_ATTR_USER); + private void parseListenForTickles(TypedXmlPullParser parser) { int userId = 0; try { - userId = Integer.parseInt(user); - } catch (NumberFormatException e) { + parser.getAttributeInt(null, XML_ATTR_USER); + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the user for listen-for-tickles", e); - } catch (NullPointerException e) { - Slog.e(TAG, "the user in listen-for-tickles is null", e); } - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); - boolean listen = enabled == null || Boolean.parseBoolean(enabled); + boolean listen = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); mMasterSyncAutomatically.put(userId, listen); } - private AuthorityInfo parseAuthority(XmlPullParser parser, int version, - AccountAuthorityValidator validator) { + private AuthorityInfo parseAuthority(TypedXmlPullParser parser, int version, + AccountAuthorityValidator validator) throws XmlPullParserException { AuthorityInfo authority = null; int id = -1; try { - id = Integer.parseInt(parser.getAttributeValue(null, "id")); + id = parser.getAttributeInt(null, "id"); } catch (NumberFormatException e) { Slog.e(TAG, "error parsing the id of the authority", e); } catch (NullPointerException e) { @@ -1832,14 +1811,13 @@ public class SyncStorageEngine { } if (id >= 0) { String authorityName = parser.getAttributeValue(null, "authority"); - String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED); + boolean enabled = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true); String syncable = parser.getAttributeValue(null, "syncable"); String accountName = parser.getAttributeValue(null, "account"); String accountType = parser.getAttributeValue(null, "type"); - String user = parser.getAttributeValue(null, XML_ATTR_USER); + int userId = parser.getAttributeInt(null, XML_ATTR_USER, 0); String packageName = parser.getAttributeValue(null, "package"); String className = parser.getAttributeValue(null, "class"); - int userId = user == null ? 0 : Integer.parseInt(user); if (accountType == null && packageName == null) { accountType = "com.google"; syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED); @@ -1884,7 +1862,7 @@ public class SyncStorageEngine { } } if (authority != null) { - authority.enabled = enabled == null || Boolean.parseBoolean(enabled); + authority.enabled = enabled; try { authority.syncable = (syncable == null) ? AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable); @@ -1912,32 +1890,22 @@ public class SyncStorageEngine { /** * Parse a periodic sync from accounts.xml. Sets the bundle to be empty. */ - private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) { + private PeriodicSync parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo) { Bundle extras = new Bundle(); // Gets filled in later. - String periodValue = parser.getAttributeValue(null, "period"); - String flexValue = parser.getAttributeValue(null, "flex"); - final long period; + long period; long flextime; try { - period = Long.parseLong(periodValue); - } catch (NumberFormatException e) { + period = parser.getAttributeLong(null, "period"); + } catch (XmlPullParserException e) { Slog.e(TAG, "error parsing the period of a periodic sync", e); return null; - } catch (NullPointerException e) { - Slog.e(TAG, "the period of a periodic sync is null", e); - return null; } try { - flextime = Long.parseLong(flexValue); - } catch (NumberFormatException e) { - flextime = calculateDefaultFlexTime(period); - Slog.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue - + ", using default: " - + flextime); - } catch (NullPointerException expected) { + flextime = parser.getAttributeLong(null, "flex"); + } catch (XmlPullParserException e) { flextime = calculateDefaultFlexTime(period); - Slog.d(TAG, "No flex time specified for this sync, using a default. period: " - + period + " flex: " + flextime); + Slog.e(TAG, "Error formatting value parsed for periodic sync flex, using default: " + + flextime, e); } PeriodicSync periodicSync; periodicSync = @@ -1949,7 +1917,7 @@ public class SyncStorageEngine { return periodicSync; } - private void parseExtra(XmlPullParser parser, Bundle extras) { + private void parseExtra(TypedXmlPullParser parser, Bundle extras) { String name = parser.getAttributeValue(null, "name"); String type = parser.getAttributeValue(null, "type"); String value1 = parser.getAttributeValue(null, "value1"); @@ -1994,9 +1962,9 @@ public class SyncStorageEngine { out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); out.startTag(null, "accounts"); - out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION)); - out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId)); - out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset)); + out.attributeInt(null, "version", ACCOUNTS_VERSION); + out.attributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, mNextAuthorityId); + out.attributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, mSyncRandomOffset); // Write the Sync Automatically flags for each user final int M = mMasterSyncAutomatically.size(); @@ -2004,8 +1972,8 @@ public class SyncStorageEngine { int userId = mMasterSyncAutomatically.keyAt(m); Boolean listen = mMasterSyncAutomatically.valueAt(m); out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES); - out.attribute(null, XML_ATTR_USER, Integer.toString(userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen)); + out.attributeInt(null, XML_ATTR_USER, userId); + out.attributeBoolean(null, XML_ATTR_ENABLED, listen); out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES); } @@ -2014,13 +1982,13 @@ public class SyncStorageEngine { AuthorityInfo authority = mAuthorities.valueAt(i); EndPoint info = authority.target; out.startTag(null, "authority"); - out.attribute(null, "id", Integer.toString(authority.ident)); - out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId)); - out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled)); + out.attributeInt(null, "id", authority.ident); + out.attributeInt(null, XML_ATTR_USER, info.userId); + out.attributeBoolean(null, XML_ATTR_ENABLED, authority.enabled); out.attribute(null, "account", info.account.name); out.attribute(null, "type", info.account.type); out.attribute(null, "authority", info.provider); - out.attribute(null, "syncable", Integer.toString(authority.syncable)); + out.attributeInt(null, "syncable", authority.syncable); out.endTag(null, "authority"); } out.endTag(null, "accounts"); diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java index 928799b31df8..0f1e6668ceec 100644 --- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java +++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java @@ -27,17 +27,14 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.format.DateTimeParseException; import java.util.ArrayDeque; @@ -179,7 +176,7 @@ public class AmbientBrightnessStatsTracker { entry.getKey()); if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) { out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS); - out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber)); + out.attributeInt(null, ATTR_USER, userSerialNumber); out.attribute(null, ATTR_LOCAL_DATE, userDayStats.getLocalDate().toString()); StringBuilder bucketBoundariesValues = new StringBuilder(); @@ -229,7 +226,7 @@ public class AmbientBrightnessStatsTracker { } tag = parser.getName(); if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) { - String userSerialNumber = parser.getAttributeValue(null, ATTR_USER); + int userSerialNumber = parser.getAttributeInt(null, ATTR_USER); LocalDate localDate = LocalDate.parse( parser.getAttributeValue(null, ATTR_LOCAL_DATE)); String[] bucketBoundaries = parser.getAttributeValue(null, @@ -246,8 +243,7 @@ public class AmbientBrightnessStatsTracker { parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]); parsedBucketStats[i] = Float.parseFloat(bucketStats[i]); } - int userId = mInjector.getUserId(mUserManager, - Integer.parseInt(userSerialNumber)); + int userId = mInjector.getUserId(mUserManager, userSerialNumber); if (userId != -1 && localDate.isAfter(cutOffDate)) { Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats( parsedStats, userId); diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 3ae99ef3ed5e..2a0e21919704 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -555,22 +555,22 @@ public class BrightnessTracker { if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) { mEvents.append(toWrite[i]); out.startTag(null, TAG_EVENT); - out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness)); - out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp)); + out.attributeFloat(null, ATTR_NITS, toWrite[i].brightness); + out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp); out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName); - out.attribute(null, ATTR_USER, Integer.toString(userSerialNo)); - out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel)); - out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode)); - out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString( - toWrite[i].colorTemperature)); - out.attribute(null, ATTR_LAST_NITS, - Float.toString(toWrite[i].lastBrightness)); - out.attribute(null, ATTR_DEFAULT_CONFIG, - Boolean.toString(toWrite[i].isDefaultBrightnessConfig)); - out.attribute(null, ATTR_POWER_SAVE, - Float.toString(toWrite[i].powerBrightnessFactor)); - out.attribute(null, ATTR_USER_POINT, - Boolean.toString(toWrite[i].isUserSetBrightness)); + out.attributeInt(null, ATTR_USER, userSerialNo); + out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel); + out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode); + out.attributeInt(null, ATTR_COLOR_TEMPERATURE, + toWrite[i].colorTemperature); + out.attributeFloat(null, ATTR_LAST_NITS, + toWrite[i].lastBrightness); + out.attributeBoolean(null, ATTR_DEFAULT_CONFIG, + toWrite[i].isDefaultBrightnessConfig); + out.attributeFloat(null, ATTR_POWER_SAVE, + toWrite[i].powerBrightnessFactor); + out.attributeBoolean(null, ATTR_USER_POINT, + toWrite[i].isUserSetBrightness); StringBuilder luxValues = new StringBuilder(); StringBuilder luxTimestamps = new StringBuilder(); for (int j = 0; j < toWrite[i].luxValues.length; ++j) { @@ -585,8 +585,8 @@ public class BrightnessTracker { out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString()); if (toWrite[i].colorValueBuckets != null && toWrite[i].colorValueBuckets.length > 0) { - out.attribute(null, ATTR_COLOR_SAMPLE_DURATION, - Long.toString(toWrite[i].colorSampleDuration)); + out.attributeLong(null, ATTR_COLOR_SAMPLE_DURATION, + toWrite[i].colorSampleDuration); StringBuilder buckets = new StringBuilder(); for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) { if (j > 0) { @@ -633,22 +633,16 @@ public class BrightnessTracker { if (TAG_EVENT.equals(tag)) { BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder(); - String brightness = parser.getAttributeValue(null, ATTR_NITS); - builder.setBrightness(Float.parseFloat(brightness)); - String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP); - builder.setTimeStamp(Long.parseLong(timestamp)); + builder.setBrightness(parser.getAttributeFloat(null, ATTR_NITS)); + builder.setTimeStamp(parser.getAttributeLong(null, ATTR_TIMESTAMP)); builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME)); - String user = parser.getAttributeValue(null, ATTR_USER); - builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user))); - String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL); - builder.setBatteryLevel(Float.parseFloat(batteryLevel)); - String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE); - builder.setNightMode(Boolean.parseBoolean(nightMode)); - String colorTemperature = - parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE); - builder.setColorTemperature(Integer.parseInt(colorTemperature)); - String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS); - builder.setLastBrightness(Float.parseFloat(lastBrightness)); + builder.setUserId(mInjector.getUserId(mUserManager, + parser.getAttributeInt(null, ATTR_USER))); + builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL)); + builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE)); + builder.setColorTemperature( + parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE)); + builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS)); String luxValue = parser.getAttributeValue(null, ATTR_LUX); String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS); @@ -667,20 +661,12 @@ public class BrightnessTracker { builder.setLuxValues(luxValues); builder.setLuxTimestamps(luxTimestamps); - String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG); - if (defaultConfig != null) { - builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig)); - } - String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE); - if (powerSave != null) { - builder.setPowerBrightnessFactor(Float.parseFloat(powerSave)); - } else { - builder.setPowerBrightnessFactor(1.0f); - } - String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT); - if (userPoint != null) { - builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint)); - } + builder.setIsDefaultBrightnessConfig( + parser.getAttributeBoolean(null, ATTR_DEFAULT_CONFIG, false)); + builder.setPowerBrightnessFactor( + parser.getAttributeFloat(null, ATTR_POWER_SAVE, 1.0f)); + builder.setUserBrightnessPoint( + parser.getAttributeBoolean(null, ATTR_USER_POINT, false)); String colorSampleDurationString = parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 1aced07e0997..329081a8391f 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -38,6 +38,7 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; +import android.util.IndentingPrintWriter; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -49,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -68,9 +70,11 @@ public class DisplayModeDirector { private static final boolean DEBUG = false; private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1; - private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2; + private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2; private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3; - private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4; + private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4; + private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5; + private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6; // Special ID used to indicate that given vote is to be applied globally, rather than to a // specific display. @@ -83,6 +87,13 @@ public class DisplayModeDirector { private final Context mContext; private final DisplayModeDirectorHandler mHandler; + private final Injector mInjector; + + private final AppRequestObserver mAppRequestObserver; + private final SettingsObserver mSettingsObserver; + private final DisplayObserver mDisplayObserver; + private final DeviceConfigInterface mDeviceConfig; + private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; // A map from the display ID to the collection of votes and their priority. The latter takes // the form of another map from the priority to the vote itself so that each priority is @@ -93,12 +104,8 @@ public class DisplayModeDirector { // A map from the display ID to the default mode of that display. private SparseArray<Display.Mode> mDefaultModeByDisplay; - private final AppRequestObserver mAppRequestObserver; - private final SettingsObserver mSettingsObserver; - private final DisplayObserver mDisplayObserver; private BrightnessObserver mBrightnessObserver; - private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener; private boolean mAlwaysRespectAppRequest; @@ -127,8 +134,14 @@ public class DisplayModeDirector { private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS; public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) { + this(context, handler, new RealInjector()); + } + + public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler, + @NonNull Injector injector) { mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); + mInjector = injector; mVotesByDisplay = new SparseArray<>(); mSupportedModesByDisplay = new SparseArray<>(); mDefaultModeByDisplay = new SparseArray<>(); @@ -137,6 +150,7 @@ public class DisplayModeDirector { mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); + mDeviceConfig = injector.getDeviceConfig(); mAlwaysRespectAppRequest = false; } @@ -455,6 +469,23 @@ public class DisplayModeDirector { } /** + * Retrieve the Vote for the given display and priority. Intended only for testing purposes. + * + * @param displayId the display to query for + * @param priority the priority of the vote to return + * @return the vote corresponding to the given {@code displayId} and {@code priority}, + * or {@code null} if there isn't one + */ + @VisibleForTesting + @Nullable + Vote getVote(int displayId, int priority) { + synchronized (mLock) { + SparseArray<Vote> votes = getVotesLocked(displayId); + return votes.get(priority); + } + } + + /** * Print the object's state and debug information into the given stream. * * @param pw The stream to dump information to. @@ -586,6 +617,17 @@ public class DisplayModeDirector { } @VisibleForTesting + BrightnessObserver getBrightnessObserver() { + return mBrightnessObserver; + } + + @VisibleForTesting + SettingsObserver getSettingsObserver() { + return mSettingsObserver; + } + + + @VisibleForTesting DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings( float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) { synchronized (mLock) { @@ -613,16 +655,35 @@ public class DisplayModeDirector { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_BRIGHTNESS_THRESHOLDS_CHANGED: + case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: { + Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; + mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged( + thresholds.first, thresholds.second); + break; + } + + case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: { + int refreshRateInZone = msg.arg1; + mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged( + refreshRateInZone); + break; + } + + case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: { Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj; - if (thresholds != null) { - mBrightnessObserver.onDeviceConfigThresholdsChanged( - thresholds.first, thresholds.second); - } else { - mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null); - } + mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged( + thresholds.first, thresholds.second); + + break; + } + + case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: { + int refreshRateInZone = msg.arg1; + mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged( + refreshRateInZone); break; + } case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED: Float defaultPeakRefreshRate = (Float) msg.obj; @@ -630,12 +691,6 @@ public class DisplayModeDirector { defaultPeakRefreshRate); break; - case MSG_REFRESH_RATE_IN_ZONE_CHANGED: - int refreshRateInZone = msg.arg1; - mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged( - refreshRateInZone); - break; - case MSG_REFRESH_RATE_RANGE_CHANGED: DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener = (DesiredDisplayModeSpecsListener) msg.obj; @@ -822,10 +877,11 @@ public class DisplayModeDirector { // by all other considerations. It acts to set a default frame rate for a device. public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0; - // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. + // FLICKER votes for a single refresh rate like [60,60], [90,90] or null. // If the higher voters result is a range, it will fix the rate to a single choice. - // It's used to avoid rate switch in certain conditions. - public static final int PRIORITY_LOW_BRIGHTNESS = 1; + // It's used to avoid refresh rate switches in certain conditions which may result in the + // user seeing the display flickering when the switches occur. + public static final int PRIORITY_FLICKER = 1; // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate. // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] @@ -898,8 +954,8 @@ public class DisplayModeDirector { switch (priority) { case PRIORITY_DEFAULT_REFRESH_RATE: return "PRIORITY_DEFAULT_REFRESH_RATE"; - case PRIORITY_LOW_BRIGHTNESS: - return "PRIORITY_LOW_BRIGHTNESS"; + case PRIORITY_FLICKER: + return "PRIORITY_FLICKER"; case PRIORITY_USER_SETTING_MIN_REFRESH_RATE: return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE"; case PRIORITY_APP_REQUEST_REFRESH_RATE: @@ -924,7 +980,8 @@ public class DisplayModeDirector { } } - private final class SettingsObserver extends ContentObserver { + @VisibleForTesting + final class SettingsObserver extends ContentObserver { private final Uri mPeakRefreshRateSetting = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); private final Uri mMinRefreshRateSetting = @@ -949,8 +1006,7 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this, - UserHandle.USER_SYSTEM); + mInjector.registerPeakRefreshRateObserver(cr, this); cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, @@ -971,6 +1027,13 @@ public class DisplayModeDirector { } } + public void setDefaultRefreshRate(float refreshRate) { + synchronized (mLock) { + mDefaultRefreshRate = refreshRate; + updateRefreshRateSettingLocked(); + } + } + public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) { if (defaultPeakRefreshRate == null) { defaultPeakRefreshRate = (float) mContext.getResources().getInteger( @@ -1189,6 +1252,7 @@ public class DisplayModeDirector { @Override public void onDisplayChanged(int displayId) { updateDisplayModes(displayId); + // TODO: Break the coupling between DisplayObserver and BrightnessObserver. mBrightnessObserver.onDisplayChanged(displayId); } @@ -1227,15 +1291,16 @@ public class DisplayModeDirector { */ @VisibleForTesting public class BrightnessObserver extends ContentObserver { - // TODO: brightnessfloat: change this to the float setting - private final Uri mDisplayBrightnessSetting = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); private final static int LIGHT_SENSOR_RATE_MS = 250; - private int[] mDisplayBrightnessThresholds; - private int[] mAmbientBrightnessThresholds; + private int[] mLowDisplayBrightnessThresholds; + private int[] mLowAmbientBrightnessThresholds; + private int[] mHighDisplayBrightnessThresholds; + private int[] mHighAmbientBrightnessThresholds; // valid threshold if any item from the array >= 0 - private boolean mShouldObserveDisplayChange; - private boolean mShouldObserveAmbientChange; + private boolean mShouldObserveDisplayLowChange; + private boolean mShouldObserveAmbientLowChange; + private boolean mShouldObserveDisplayHighChange; + private boolean mShouldObserveAmbientHighChange; private SensorManager mSensorManager; private Sensor mLightSensor; @@ -1243,46 +1308,122 @@ public class DisplayModeDirector { // Take it as low brightness before valid sensor data comes private float mAmbientLux = -1.0f; private AmbientFilter mAmbientFilter; + private int mBrightness = -1; private final Context mContext; - // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak - // refresh rate changeable and low power mode off. After initialization, these states will + // Enable light sensor only when mShouldObserveAmbientLowChange is true or + // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate + // changeable and low power mode off. After initialization, these states will // be updated from the same handler thread. - private boolean mScreenOn = false; + private boolean mDefaultDisplayOn = false; private boolean mRefreshRateChangeable = false; private boolean mLowPowerModeEnabled = false; - private int mRefreshRateInZone; + private int mRefreshRateInLowZone; + private int mRefreshRateInHighZone; BrightnessObserver(Context context, Handler handler) { super(handler); mContext = context; - mDisplayBrightnessThresholds = context.getResources().getIntArray( + mLowDisplayBrightnessThresholds = context.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); - mAmbientBrightnessThresholds = context.getResources().getIntArray( + mLowAmbientBrightnessThresholds = context.getResources().getIntArray( R.array.config_ambientThresholdsOfPeakRefreshRate); - if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) { - throw new RuntimeException("display brightness threshold array and ambient " - + "brightness threshold array have different length"); + if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) { + throw new RuntimeException("display low brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "displayBrightnessThresholds=" + + Arrays.toString(mLowDisplayBrightnessThresholds) + + ", ambientBrightnessThresholds=" + + Arrays.toString(mLowAmbientBrightnessThresholds)); } + + mHighDisplayBrightnessThresholds = context.getResources().getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + mHighAmbientBrightnessThresholds = context.getResources().getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + if (mHighDisplayBrightnessThresholds.length + != mHighAmbientBrightnessThresholds.length) { + throw new RuntimeException("display high brightness threshold array and ambient " + + "brightness threshold array have different length: " + + "displayBrightnessThresholds=" + + Arrays.toString(mHighDisplayBrightnessThresholds) + + ", ambientBrightnessThresholds=" + + Arrays.toString(mHighAmbientBrightnessThresholds)); + } + mRefreshRateInHighZone = context.getResources().getInteger( + R.integer.config_fixedRefreshRateInHighZone); + } + + /** + * @return the refresh to lock to when in a low brightness zone + */ + @VisibleForTesting + int getRefreshRateInLowZone() { + return mRefreshRateInLowZone; + } + + /** + * @return the display brightness thresholds for the low brightness zones + */ + @VisibleForTesting + int[] getLowDisplayBrightnessThresholds() { + return mLowDisplayBrightnessThresholds; + } + + /** + * @return the ambient brightness thresholds for the low brightness zones + */ + @VisibleForTesting + int[] getLowAmbientBrightnessThresholds() { + return mLowAmbientBrightnessThresholds; + } + + public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) { + mSensorManager = sensorManager; + mLightSensor = lightSensor; + + mSensorManager.registerListener(mLightSensorListener, + mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); } public void observe(SensorManager sensorManager) { mSensorManager = sensorManager; + final ContentResolver cr = mContext.getContentResolver(); + mBrightness = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); // DeviceConfig is accessible after system ready. - int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds(); - int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds(); + int[] lowDisplayBrightnessThresholds = + mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(); + int[] lowAmbientBrightnessThresholds = + mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(); + + if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null + && lowDisplayBrightnessThresholds.length + == lowAmbientBrightnessThresholds.length) { + mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds; + mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds; + } - if (brightnessThresholds != null && ambientThresholds != null - && brightnessThresholds.length == ambientThresholds.length) { - mDisplayBrightnessThresholds = brightnessThresholds; - mAmbientBrightnessThresholds = ambientThresholds; + + int[] highDisplayBrightnessThresholds = + mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(); + int[] highAmbientBrightnessThresholds = + mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(); + + if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null + && highDisplayBrightnessThresholds.length + == highAmbientBrightnessThresholds.length) { + mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds; + mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds; } - mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone(); + mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone(); + mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone(); + restartObserver(); mDeviceConfigDisplaySettings.startListening(); } @@ -1294,7 +1435,7 @@ public class DisplayModeDirector { updateSensorStatus(); if (!changeable) { // Revoke previous vote from BrightnessObserver - updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null); + updateVoteLocked(Vote.PRIORITY_FLICKER, null); } } } @@ -1306,25 +1447,48 @@ public class DisplayModeDirector { } } - public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds, + public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { - if (brightnessThresholds != null && ambientThresholds != null - && brightnessThresholds.length == ambientThresholds.length) { - mDisplayBrightnessThresholds = brightnessThresholds; - mAmbientBrightnessThresholds = ambientThresholds; + if (displayThresholds != null && ambientThresholds != null + && displayThresholds.length == ambientThresholds.length) { + mLowDisplayBrightnessThresholds = displayThresholds; + mLowAmbientBrightnessThresholds = ambientThresholds; } else { // Invalid or empty. Use device default. - mDisplayBrightnessThresholds = mContext.getResources().getIntArray( + mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray( R.array.config_brightnessThresholdsOfPeakRefreshRate); - mAmbientBrightnessThresholds = mContext.getResources().getIntArray( + mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray( R.array.config_ambientThresholdsOfPeakRefreshRate); } restartObserver(); } - public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) { - if (refreshRate != mRefreshRateInZone) { - mRefreshRateInZone = refreshRate; + public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) { + if (refreshRate != mRefreshRateInLowZone) { + mRefreshRateInLowZone = refreshRate; + restartObserver(); + } + } + + public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, + int[] ambientThresholds) { + if (displayThresholds != null && ambientThresholds != null + && displayThresholds.length == ambientThresholds.length) { + mHighDisplayBrightnessThresholds = displayThresholds; + mHighAmbientBrightnessThresholds = ambientThresholds; + } else { + // Invalid or empty. Use device default. + mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate); + mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray( + R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate); + } + restartObserver(); + } + + public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) { + if (refreshRate != mRefreshRateInHighZone) { + mRefreshRateInHighZone = refreshRate; restartObserver(); } } @@ -1332,48 +1496,95 @@ public class DisplayModeDirector { public void dumpLocked(PrintWriter pw) { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); - pw.println(" mRefreshRateInZone: " + mRefreshRateInZone); + pw.println(" mBrightness: " + mBrightness); + pw.println(" mDefaultDisplayOn: " + mDefaultDisplayOn); + pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled); + pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable); + pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange); + pw.println(" mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange); + pw.println(" mRefreshRateInLowZone: " + mRefreshRateInLowZone); - for (int d: mDisplayBrightnessThresholds) { - pw.println(" mDisplayBrightnessThreshold: " + d); + for (int d : mLowDisplayBrightnessThresholds) { + pw.println(" mDisplayLowBrightnessThreshold: " + d); } - for (int d: mAmbientBrightnessThresholds) { - pw.println(" mAmbientBrightnessThreshold: " + d); + for (int d : mLowAmbientBrightnessThresholds) { + pw.println(" mAmbientLowBrightnessThreshold: " + d); + } + + pw.println(" mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange); + pw.println(" mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange); + pw.println(" mRefreshRateInHighZone: " + mRefreshRateInHighZone); + + for (int d : mHighDisplayBrightnessThresholds) { + pw.println(" mDisplayHighBrightnessThresholds: " + d); + } + + for (int d : mHighAmbientBrightnessThresholds) { + pw.println(" mAmbientHighBrightnessThresholds: " + d); } mLightSensorListener.dumpLocked(pw); + + if (mAmbientFilter != null) { + IndentingPrintWriter ipw = new IndentingPrintWriter(pw); + ipw.setIndent(" "); + mAmbientFilter.dump(ipw); + } } public void onDisplayChanged(int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { - onScreenOn(isDefaultDisplayOn()); + updateDefaultDisplayState(); } } @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - onBrightnessChangedLocked(); + final ContentResolver cr = mContext.getContentResolver(); + int brightness = Settings.System.getIntForUser(cr, + Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId()); + if (brightness != mBrightness) { + mBrightness = brightness; + onBrightnessChangedLocked(); + } } } private void restartObserver() { - mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds); - mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds); - final ContentResolver cr = mContext.getContentResolver(); - if (mShouldObserveDisplayChange) { + + if (mRefreshRateInLowZone > 0) { + mShouldObserveDisplayLowChange = hasValidThreshold( + mLowDisplayBrightnessThresholds); + mShouldObserveAmbientLowChange = hasValidThreshold( + mLowAmbientBrightnessThresholds); + } else { + mShouldObserveDisplayLowChange = false; + mShouldObserveAmbientLowChange = false; + } + + if (mRefreshRateInHighZone > 0) { + mShouldObserveDisplayHighChange = hasValidThreshold( + mHighDisplayBrightnessThresholds); + mShouldObserveAmbientHighChange = hasValidThreshold( + mHighAmbientBrightnessThresholds); + } else { + mShouldObserveDisplayHighChange = false; + mShouldObserveAmbientHighChange = false; + } + + if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) { // Content Service does not check if an listener has already been registered. // To ensure only one listener is registered, force an unregistration first. - cr.unregisterContentObserver(this); - cr.registerContentObserver(mDisplayBrightnessSetting, - false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); + mInjector.unregisterBrightnessObserver(cr, this); + mInjector.registerBrightnessObserver(cr, this); } else { - cr.unregisterContentObserver(this); + mInjector.unregisterBrightnessObserver(cr, this); } - if (mShouldObserveAmbientChange) { + if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) { Resources resources = mContext.getResources(); String lightSensorType = resources.getString( com.android.internal.R.string.config_displayLightSensorType); @@ -1399,8 +1610,6 @@ public class DisplayModeDirector { mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res); mLightSensor = lightSensor; - - onScreenOn(isDefaultDisplayOn()); } } else { mAmbientFilter = null; @@ -1419,11 +1628,7 @@ public class DisplayModeDirector { * Checks to see if at least one value is positive, in which case it is necessary to listen * to value changes. */ - private boolean checkShouldObserve(int[] a) { - if (mRefreshRateInZone <= 0) { - return false; - } - + private boolean hasValidThreshold(int[] a) { for (int d: a) { if (d >= 0) { return true; @@ -1433,13 +1638,13 @@ public class DisplayModeDirector { return false; } - private boolean isInsideZone(int brightness, float lux) { - for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) { - int disp = mDisplayBrightnessThresholds[i]; - int ambi = mAmbientBrightnessThresholds[i]; + private boolean isInsideLowZone(int brightness, float lux) { + for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) { + int disp = mLowDisplayBrightnessThresholds[i]; + int ambi = mLowAmbientBrightnessThresholds[i]; if (disp >= 0 && ambi >= 0) { - if (brightness <= disp && mAmbientLux <= ambi) { + if (brightness <= disp && lux <= ambi) { return true; } } else if (disp >= 0) { @@ -1447,7 +1652,7 @@ public class DisplayModeDirector { return true; } } else if (ambi >= 0) { - if (mAmbientLux <= ambi) { + if (lux <= ambi) { return true; } } @@ -1455,28 +1660,77 @@ public class DisplayModeDirector { return false; } - // TODO: brightnessfloat: make it use float not int - private void onBrightnessChangedLocked() { - final ContentResolver cr = mContext.getContentResolver(); - int brightness = Settings.System.getIntForUser(cr, - Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId()); + private boolean isInsideHighZone(int brightness, float lux) { + for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) { + int disp = mHighDisplayBrightnessThresholds[i]; + int ambi = mHighAmbientBrightnessThresholds[i]; + + if (disp >= 0 && ambi >= 0) { + if (brightness >= disp && lux >= ambi) { + return true; + } + } else if (disp >= 0) { + if (brightness >= disp) { + return true; + } + } else if (ambi >= 0) { + if (lux >= ambi) { + return true; + } + } + } + + return false; + } + private void onBrightnessChangedLocked() { Vote vote = null; - boolean insideZone = isInsideZone(brightness, mAmbientLux); - if (insideZone) { - vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone); + + if (mBrightness < 0) { + // Either the setting isn't available or we shouldn't be observing yet anyways. + // Either way, just bail out since there's nothing we can do here. + return; + } + + boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux); + if (insideLowZone) { + vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone); + } + + boolean insideHighZone = hasValidHighZone() + && isInsideHighZone(mBrightness, mAmbientLux); + if (insideHighZone) { + vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone); } if (DEBUG) { - Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux + - ", Vote " + vote); + Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux + + ", Vote " + vote); } - updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); + updateVoteLocked(Vote.PRIORITY_FLICKER, vote); + } + + private boolean hasValidLowZone() { + return mRefreshRateInLowZone > 0 + && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange); + } + + private boolean hasValidHighZone() { + return mRefreshRateInHighZone > 0 + && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange); + } + + private void updateDefaultDisplayState() { + Display display = mContext.getSystemService(DisplayManager.class) + .getDisplay(Display.DEFAULT_DISPLAY); + boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF; + setDefaultDisplayState(defaultDisplayOn); } - private void onScreenOn(boolean on) { - if (mScreenOn != on) { - mScreenOn = on; + @VisibleForTesting + public void setDefaultDisplayState(boolean on) { + if (mDefaultDisplayOn != on) { + mDefaultDisplayOn = on; updateSensorStatus(); } } @@ -1486,8 +1740,8 @@ public class DisplayModeDirector { return; } - if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled - && mRefreshRateChangeable) { + if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) + && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { mSensorManager.registerListener(mLightSensorListener, mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); } else { @@ -1496,11 +1750,8 @@ public class DisplayModeDirector { } } - private boolean isDefaultDisplayOn() { - final Display display = mContext.getSystemService(DisplayManager.class) - .getDisplay(Display.DEFAULT_DISPLAY); - return display.getState() != Display.STATE_OFF - && mContext.getSystemService(PowerManager.class).isInteractive(); + private boolean isDeviceActive() { + return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext); } private final class LightSensorEventListener implements SensorEventListener { @@ -1518,23 +1769,33 @@ public class DisplayModeDirector { Slog.d(TAG, "On sensor changed: " + mLastSensorData); } - boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux); - if (zoneChanged && mLastSensorData < mAmbientLux) { - // Easier to see flicker at lower brightness environment. Forget the history to - // get immediate response. - mAmbientFilter.clear(); + boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux, + mLowAmbientBrightnessThresholds); + boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux, + mHighAmbientBrightnessThresholds); + if ((lowZoneChanged && mLastSensorData < mAmbientLux) + || (highZoneChanged && mLastSensorData > mAmbientLux)) { + // Easier to see flicker at lower brightness environment or high brightness + // environment. Forget the history to get immediate response. + if (mAmbientFilter != null) { + mAmbientFilter.clear(); + } } long now = SystemClock.uptimeMillis(); - mAmbientFilter.addValue(now, mLastSensorData); + if (mAmbientFilter != null) { + mAmbientFilter.addValue(now, mLastSensorData); + } mHandler.removeCallbacks(mInjectSensorEventRunnable); processSensorData(now); - if (zoneChanged && mLastSensorData > mAmbientLux) { + if ((lowZoneChanged && mLastSensorData > mAmbientLux) + || (highZoneChanged && mLastSensorData < mAmbientLux)) { // Sensor may not report new event if there is no brightness change. // Need to keep querying the temporal filter for the latest estimation, - // until enter in higher lux zone or is interrupted by a new sensor event. + // until sensor readout and filter estimation are in the same zone or + // is interrupted by a new sensor event. mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); } } @@ -1549,17 +1810,19 @@ public class DisplayModeDirector { } private void processSensorData(long now) { - mAmbientLux = mAmbientFilter.getEstimate(now); + if (mAmbientFilter != null) { + mAmbientLux = mAmbientFilter.getEstimate(now); + } else { + mAmbientLux = mLastSensorData; + } synchronized (mLock) { onBrightnessChangedLocked(); } } - private boolean isDifferentZone(float lux1, float lux2) { - for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) { - final float boundary = mAmbientBrightnessThresholds[z]; - + private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) { + for (final float boundary : luxThresholds) { // Test each boundary. See if the current value and the new value are at // different sides. if ((lux1 <= boundary && lux2 > boundary) @@ -1579,7 +1842,10 @@ public class DisplayModeDirector { processSensorData(now); // Inject next event if there is a possible zone change. - if (isDifferentZone(mLastSensorData, mAmbientLux)) { + if (isDifferentZone(mLastSensorData, mAmbientLux, + mLowAmbientBrightnessThresholds) + || isDifferentZone(mLastSensorData, mAmbientLux, + mHighAmbientBrightnessThresholds)) { mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); } } @@ -1592,72 +1858,113 @@ public class DisplayModeDirector { } public void startListening() { - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), this); } /* * Return null if no such property or wrong format (not comma separated integers). */ - public int[] getBrightnessThresholds() { + public int[] getLowDisplayBrightnessThresholds() { return getIntArrayProperty( DisplayManager.DeviceConfig. - KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS); + KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); } /* * Return null if no such property or wrong format (not comma separated integers). */ - public int[] getAmbientThresholds() { + public int[] getLowAmbientBrightnessThresholds() { return getIntArrayProperty( DisplayManager.DeviceConfig. - KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS); + KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); + } + + public int getRefreshRateInLowZone() { + int defaultRefreshRateInZone = mContext.getResources().getInteger( + R.integer.config_defaultRefreshRateInZone); + + int refreshRate = mDeviceConfig.getInt( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, + defaultRefreshRateInZone); + + return refreshRate; } /* - * Return null if no such property + * Return null if no such property or wrong format (not comma separated integers). */ - public Float getDefaultPeakRefreshRate() { - float defaultPeakRefreshRate = DeviceConfig.getFloat( - DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); + public int[] getHighDisplayBrightnessThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS); + } - if (defaultPeakRefreshRate == -1) { - return null; - } - return defaultPeakRefreshRate; + /* + * Return null if no such property or wrong format (not comma separated integers). + */ + public int[] getHighAmbientBrightnessThresholds() { + return getIntArrayProperty( + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS); } - public int getRefreshRateInZone() { + public int getRefreshRateInHighZone() { int defaultRefreshRateInZone = mContext.getResources().getInteger( - R.integer.config_defaultRefreshRateInZone); + R.integer.config_fixedRefreshRateInHighZone); - int refreshRate = DeviceConfig.getInt( + int refreshRate = mDeviceConfig.getInt( DeviceConfig.NAMESPACE_DISPLAY_MANAGER, - DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE, + DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE, defaultRefreshRateInZone); return refreshRate; } + /* + * Return null if no such property + */ + public Float getDefaultPeakRefreshRate() { + float defaultPeakRefreshRate = mDeviceConfig.getFloat( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1); + + if (defaultPeakRefreshRate == -1) { + return null; + } + return defaultPeakRefreshRate; + } + @Override public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) { - int[] brightnessThresholds = getBrightnessThresholds(); - int[] ambientThresholds = getAmbientThresholds(); Float defaultPeakRefreshRate = getDefaultPeakRefreshRate(); - int refreshRateInZone = getRefreshRateInZone(); - - mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED, - new Pair<int[], int[]>(brightnessThresholds, ambientThresholds)) - .sendToTarget(); mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED, defaultPeakRefreshRate).sendToTarget(); - mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone, - 0).sendToTarget(); + + int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds(); + int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds(); + int refreshRateInLowZone = getRefreshRateInLowZone(); + + mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED, + new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds)) + .sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0) + .sendToTarget(); + + int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds(); + int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds(); + int refreshRateInHighZone = getRefreshRateInHighZone(); + + mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED, + new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds)) + .sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0) + .sendToTarget(); } private int[] getIntArrayProperty(String prop) { - String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, + String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop, null); if (strArray != null) { @@ -1684,4 +1991,59 @@ public class DisplayModeDirector { } } + interface Injector { + // TODO: brightnessfloat: change this to the float setting + Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + + @NonNull + DeviceConfigInterface getDeviceConfig(); + + void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + boolean isDeviceInteractive(@NonNull Context context); + } + + @VisibleForTesting + static class RealInjector implements Injector { + + @Override + @NonNull + public DeviceConfigInterface getDeviceConfig() { + return DeviceConfigInterface.REAL; + } + + @Override + public void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override + public void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.unregisterContentObserver(observer); + } + + @Override + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override + public boolean isDeviceInteractive(@NonNull Context ctx) { + return ctx.getSystemService(PowerManager.class).isInteractive(); + } + } + } diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 494dd397b1e9..b0820e81ec09 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -25,6 +25,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseLongArray; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.Display; @@ -322,7 +323,7 @@ final class PersistentDataStore { return; } - XmlPullParser parser; + TypedXmlPullParser parser; try { parser = Xml.resolvePullParser(is); loadFromXml(parser); @@ -355,7 +356,7 @@ final class PersistentDataStore { } } - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE); final int outerDepth = parser.getDepth(); @@ -375,7 +376,7 @@ final class PersistentDataStore { } } - private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser) + private void loadRememberedWifiDisplaysFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -399,7 +400,7 @@ final class PersistentDataStore { } } - private void loadDisplaysFromXml(XmlPullParser parser) + private void loadDisplaysFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -420,7 +421,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE); @@ -491,7 +492,7 @@ final class PersistentDataStore { return mColorMode; } - public void loadFromXml(XmlPullParser parser) + public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); @@ -503,7 +504,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_COLOR_MODE); serializer.text(Integer.toString(mColorMode)); serializer.endTag(null, TAG_COLOR_MODE); @@ -531,7 +532,8 @@ final class PersistentDataStore { return false; } - public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + public void loadFromXml(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { switch (parser.getName()) { @@ -545,7 +547,7 @@ final class PersistentDataStore { } } - private static int loadIntValue(XmlPullParser parser) + private static int loadIntValue(TypedXmlPullParser parser) throws IOException, XmlPullParserException { try { String value = parser.nextText(); @@ -555,7 +557,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { if (mWidth > 0 && mHeight > 0) { serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH); serializer.text(Integer.toString(mWidth)); @@ -612,14 +614,14 @@ final class PersistentDataStore { return mConfigurations.get(userSerial); } - public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException { + public void loadFromXml(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) { int userSerial; try { - userSerial = Integer.parseInt( - parser.getAttributeValue(null, ATTR_USER_SERIAL)); + userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL); } catch (NumberFormatException nfe) { userSerial = -1; Slog.e(TAG, "Failed to read in brightness configuration", nfe); @@ -655,20 +657,20 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { for (int i = 0; i < mConfigurations.size(); i++) { final int userSerial = mConfigurations.keyAt(i); final BrightnessConfiguration config = mConfigurations.valueAt(i); serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION); - serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial)); + serializer.attributeInt(null, ATTR_USER_SERIAL, userSerial); String packageName = mPackageNames.get(userSerial); if (packageName != null) { serializer.attribute(null, ATTR_PACKAGE_NAME, packageName); } long timestamp = mTimeStamps.get(userSerial, -1); if (timestamp != -1) { - serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp)); + serializer.attributeLong(null, ATTR_TIME_STAMP, timestamp); } config.saveToXml(serializer); serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION); diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java b/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java new file mode 100644 index 000000000000..e4b7b03a8e07 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerInternal.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.graphics.fonts; + +import android.annotation.Nullable; +import android.os.SharedMemory; + +/** Local interface for {@link FontManagerService}. */ +public interface FontManagerInternal { + + /** Returns a SharedMemory in which the system font map is serialized. */ + @Nullable SharedMemory getSerializedSystemFontMap(); +} diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java new file mode 100644 index 000000000000..22921ad1ecc2 --- /dev/null +++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.graphics.fonts; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Typeface; +import android.os.SharedMemory; +import android.system.ErrnoException; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import java.io.IOException; + +/** A service for managing system fonts. */ +// TODO(b/173619554): Add API to update fonts. +public final class FontManagerService { + + private static final String TAG = "FontManagerService"; + + /** Class to manage FontManagerService's lifecycle. */ + public static final class Lifecycle extends SystemService { + private final FontManagerService mService; + + public Lifecycle(@NonNull Context context) { + super(context); + mService = new FontManagerService(); + } + + @Override + public void onStart() { + LocalServices.addService(FontManagerInternal.class, + new FontManagerInternal() { + @Override + @Nullable + public SharedMemory getSerializedSystemFontMap() { + return mService.getSerializedSystemFontMap(); + } + }); + } + } + + @GuardedBy("this") + @Nullable + private SharedMemory mSerializedSystemFontMap = null; + + @Nullable + private SharedMemory getSerializedSystemFontMap() { + synchronized (FontManagerService.this) { + if (mSerializedSystemFontMap == null) { + mSerializedSystemFontMap = createSerializedSystemFontMapLocked(); + } + return mSerializedSystemFontMap; + } + } + + @Nullable + private SharedMemory createSerializedSystemFontMapLocked() { + // TODO(b/173619554): use updated fonts. + try { + return Typeface.serializeFontMap(Typeface.getSystemFontMap()); + } catch (IOException | ErrnoException e) { + Slog.e(TAG, "Failed to serialize SystemServer system font map", e); + } + return null; + } +} diff --git a/services/core/java/com/android/server/hdmi/CecMessageBuffer.java b/services/core/java/com/android/server/hdmi/CecMessageBuffer.java new file mode 100644 index 000000000000..8f971fd7db07 --- /dev/null +++ b/services/core/java/com/android/server/hdmi/CecMessageBuffer.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import java.util.ArrayList; +import java.util.List; + +/** + * Buffer for processing the incoming CEC messages while allocating logical addresses. + */ +final class CecMessageBuffer { + private List<HdmiCecMessage> mBuffer = new ArrayList<>(); + private HdmiControlService mHdmiControlService; + + CecMessageBuffer(HdmiControlService hdmiControlService) { + mHdmiControlService = hdmiControlService; + } + + /** + * Adds a message to the buffer. + * Only certain types of messages need to be buffered. + * @param message The message to add to the buffer + * @return Whether the message was added to the buffer + */ + public boolean bufferMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case Constants.MESSAGE_ACTIVE_SOURCE: + bufferActiveSource(message); + return true; + case Constants.MESSAGE_IMAGE_VIEW_ON: + case Constants.MESSAGE_TEXT_VIEW_ON: + bufferImageOrTextViewOn(message); + return true; + case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST: + bufferSystemAudioModeRequest(message); + return true; + // Add here if new message that needs to buffer + default: + // Do not need to buffer messages other than above + return false; + } + } + + /** + * Process all messages in the buffer. + */ + public void processMessages() { + for (final HdmiCecMessage message : mBuffer) { + mHdmiControlService.runOnServiceThread(new Runnable() { + @Override + public void run() { + mHdmiControlService.handleCecCommand(message); + } + }); + } + mBuffer.clear(); + } + + private void bufferActiveSource(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) { + mBuffer.add(message); + } + } + + private void bufferImageOrTextViewOn(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) + && !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) { + mBuffer.add(message); + } + } + + private void bufferSystemAudioModeRequest(HdmiCecMessage message) { + if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) { + mBuffer.add(message); + } + } + + // Returns true if the message is replaced + private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) { + for (int i = 0; i < mBuffer.size(); i++) { + HdmiCecMessage bufferedMessage = mBuffer.get(i); + if (bufferedMessage.getOpcode() == opcode) { + mBuffer.set(i, message); + return true; + } + } + return false; + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java index 2374ece1dd65..a261fa1f2741 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java @@ -22,19 +22,12 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; -import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; -import android.database.ContentObserver; import android.hardware.hdmi.HdmiControlManager; -import android.net.Uri; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; import android.os.SystemProperties; -import android.os.UserHandle; import android.provider.Settings.Global; -import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -95,23 +88,6 @@ public class HdmiCecConfig { @Nullable private final CecSettings mProductConfig; @Nullable private final CecSettings mVendorOverride; - private final ArrayMap<Setting, Set<SettingChangeListener>> - mSettingChangeListeners = new ArrayMap<>(); - - private SettingsObserver mSettingsObserver; - - /** - * Listener used to get notifications when value of a setting changes. - */ - public interface SettingChangeListener { - /** - * Called when value of a setting changes. - * - * @param setting name of a CEC setting that changed - */ - void onChange(@NonNull @CecSettingName String setting); - } - /** * Setting storage input/output helper class. */ @@ -183,18 +159,6 @@ public class HdmiCecConfig { } } - private class SettingsObserver extends ContentObserver { - SettingsObserver(Handler handler) { - super(handler); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - String setting = uri.getLastPathSegment(); - HdmiCecConfig.this.notifyGlobalSettingChanged(setting); - } - } - @VisibleForTesting HdmiCecConfig(@NonNull Context context, @NonNull StorageAdapter storageAdapter, @@ -347,7 +311,6 @@ public class HdmiCecConfig { } else if (storage == STORAGE_SHARED_PREFS) { Slog.d(TAG, "Setting '" + storageKey + "' shared pref."); mStorageAdapter.storeSharedPref(storageKey, value); - notifySettingChanged(setting); } } @@ -355,103 +318,6 @@ public class HdmiCecConfig { return Integer.decode(value.getIntValue()); } - private void notifyGlobalSettingChanged(String setting) { - switch (setting) { - case Global.HDMI_CONTROL_ENABLED: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); - break; - case Global.HDMI_CEC_VERSION: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION); - break; - case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP: - notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_SEND_STANDBY_ON_SLEEP); - break; - } - } - - private void notifySettingChanged(@NonNull @CecSettingName String name) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - notifySettingChanged(setting); - } - - private void notifySettingChanged(@NonNull Setting setting) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); - if (listeners == null) { - return; // No listeners registered, do nothing. - } - for (SettingChangeListener listener: listeners) { - listener.onChange(setting.getName()); - } - } - - /** - * This method registers Global Setting change observer. - * Needs to be called once after initialization of HdmiCecConfig. - */ - public void registerGlobalSettingsObserver(Looper looper) { - Handler handler = new Handler(looper); - mSettingsObserver = new SettingsObserver(handler); - ContentResolver resolver = mContext.getContentResolver(); - String[] settings = new String[] { - Global.HDMI_CONTROL_ENABLED, - Global.HDMI_CEC_VERSION, - Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP, - }; - for (String setting: settings) { - resolver.registerContentObserver(Global.getUriFor(setting), false, - mSettingsObserver, UserHandle.USER_ALL); - } - } - - /** - * This method unregisters Global Setting change observer. - */ - public void unregisterGlobalSettingsObserver() { - ContentResolver resolver = mContext.getContentResolver(); - resolver.unregisterContentObserver(mSettingsObserver); - } - - /** - * Register change listener for a given setting name. - */ - public void registerChangeListener(@NonNull @CecSettingName String name, - SettingChangeListener listener) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - @Storage int storage = getStorage(setting); - if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) { - throw new IllegalArgumentException("Change listeners for setting '" + name - + "' not supported."); - } - if (!mSettingChangeListeners.containsKey(setting)) { - mSettingChangeListeners.put(setting, new HashSet<>()); - } - mSettingChangeListeners.get(setting).add(listener); - } - - /** - * Remove change listener for a given setting name. - */ - public void removeChangeListener(@NonNull @CecSettingName String name, - SettingChangeListener listener) { - Setting setting = getSetting(name); - if (setting == null) { - throw new IllegalArgumentException("Setting '" + name + "' does not exist."); - } - if (mSettingChangeListeners.containsKey(setting)) { - Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting); - listeners.remove(listener); - if (listeners.isEmpty()) { - mSettingChangeListeners.remove(setting); - } - } - } - /** * Returns a list of all settings based on the XML metadata. */ diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 11e18c54b1e4..ad3773e78b6a 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -1186,10 +1186,11 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { // When the device is not unplugged but reawaken from standby, we check if the System // Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly. if (getAvrDeviceInfo() != null && portId == getAvrDeviceInfo().getPortId()) { + HdmiLogger.debug("Port ID:%d, 5v=%b", portId, connected); if (!connected) { setSystemAudioMode(false); - } else if (mSystemAudioControlFeatureEnabled != mService.isSystemAudioActivated()){ - setSystemAudioMode(mSystemAudioControlFeatureEnabled); + } else { + onNewAvrAdded(getAvrDeviceInfo()); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index fd825d6f6288..56b73ba04d89 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -405,74 +405,7 @@ public class HdmiControlService extends SystemService { // Use getAtomWriter() instead of accessing directly, to allow dependency injection for testing. private HdmiCecAtomWriter mAtomWriter = new HdmiCecAtomWriter(); - // Buffer for processing the incoming cec messages while allocating logical addresses. - private final class CecMessageBuffer { - private List<HdmiCecMessage> mBuffer = new ArrayList<>(); - - public boolean bufferMessage(HdmiCecMessage message) { - switch (message.getOpcode()) { - case Constants.MESSAGE_ACTIVE_SOURCE: - bufferActiveSource(message); - return true; - case Constants.MESSAGE_IMAGE_VIEW_ON: - case Constants.MESSAGE_TEXT_VIEW_ON: - bufferImageOrTextViewOn(message); - return true; - case Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST: - bufferSystemAudioModeRequest(message); - return true; - // Add here if new message that needs to buffer - default: - // Do not need to buffer messages other than above - return false; - } - } - - public void processMessages() { - for (final HdmiCecMessage message : mBuffer) { - runOnServiceThread(new Runnable() { - @Override - public void run() { - handleCecCommand(message); - } - }); - } - mBuffer.clear(); - } - - private void bufferActiveSource(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_ACTIVE_SOURCE)) { - mBuffer.add(message); - } - } - - private void bufferImageOrTextViewOn(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_IMAGE_VIEW_ON) && - !replaceMessageIfBuffered(message, Constants.MESSAGE_TEXT_VIEW_ON)) { - mBuffer.add(message); - } - } - - private void bufferSystemAudioModeRequest(HdmiCecMessage message) { - if (!replaceMessageIfBuffered(message, Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST)) { - mBuffer.add(message); - } - } - - // Returns true if the message is replaced - private boolean replaceMessageIfBuffered(HdmiCecMessage message, int opcode) { - for (int i = 0; i < mBuffer.size(); i++) { - HdmiCecMessage bufferedMessage = mBuffer.get(i); - if (bufferedMessage.getOpcode() == opcode) { - mBuffer.set(i, message); - return true; - } - } - return false; - } - } - - private final CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer(); + private CecMessageBuffer mCecMessageBuffer = new CecMessageBuffer(this); private final SelectRequestBuffer mSelectRequestBuffer = new SelectRequestBuffer(); @@ -563,7 +496,6 @@ public class HdmiControlService extends SystemService { if (mMessageValidator == null) { mMessageValidator = new HdmiCecMessageValidator(this); } - mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper); } private void bootCompleted() { @@ -988,6 +920,11 @@ public class HdmiControlService extends SystemService { mMessageValidator = messageValidator; } + @VisibleForTesting + void setCecMessageBuffer(CecMessageBuffer cecMessageBuffer) { + this.mCecMessageBuffer = cecMessageBuffer; + } + /** * Returns {@link Looper} of main thread. Use this {@link Looper} instance * for tasks that are running on main service thread. diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index e47b54472192..52a804a36750 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -25,6 +25,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.util.HexDump; @@ -571,14 +572,13 @@ final class HdmiUtils { // return a list of devices config public static List<DeviceConfig> parse(InputStream in) throws XmlPullParserException, IOException { - XmlPullParser parser = Xml.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parser.nextTag(); return readDevices(parser); } - private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + private static void skip(TypedXmlPullParser parser) + throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } @@ -595,7 +595,7 @@ final class HdmiUtils { } } - private static List<DeviceConfig> readDevices(XmlPullParser parser) + private static List<DeviceConfig> readDevices(TypedXmlPullParser parser) throws XmlPullParserException, IOException { List<DeviceConfig> devices = new ArrayList<>(); @@ -624,7 +624,7 @@ final class HdmiUtils { // Processes device tags in the config. @Nullable - private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType) + private static DeviceConfig readDeviceConfig(TypedXmlPullParser parser, String deviceType) throws XmlPullParserException, IOException { List<CodecSad> codecSads = new ArrayList<>(); int format; diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index 6633789ffc06..db93ad0617ff 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -102,12 +102,12 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction { } private void setArcStatus(boolean enabled) { - boolean wasEnabled = tv().setArcStatus(enabled); - Slog.i(TAG, "Change arc status [old:" + wasEnabled + ", new:" + enabled + "]"); + tv().setArcStatus(enabled); + Slog.i(TAG, "Change arc status to " + enabled); // If enabled before and set to "disabled" and send <Report Arc Terminated> to // av reciever. - if (!enabled && wasEnabled) { + if (!enabled) { sendCommand(HdmiCecMessageBuilder.buildReportArcTerminated(getSourceAddress(), mAvrAddress)); } diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java index 3888b1b71096..0563806895a0 100644 --- a/services/core/java/com/android/server/input/ConfigurationProcessor.java +++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java @@ -18,15 +18,13 @@ package com.android.server.input; import android.text.TextUtils; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; -import org.xmlpull.v1.XmlPullParser; - import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,9 +36,8 @@ class ConfigurationProcessor { static List<String> processExcludedDeviceNames(InputStream xml) throws Exception { List<String> names = new ArrayList<>(); - try (InputStreamReader confReader = new InputStreamReader(xml)) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(confReader); + { + TypedXmlPullParser parser = Xml.resolvePullParser(xml); XmlUtils.beginDocument(parser, "devices"); while (true) { XmlUtils.nextElement(parser); @@ -90,9 +87,8 @@ class ConfigurationProcessor { static Map<String, Integer> processInputPortAssociations(InputStream xml) throws Exception { Map<String, Integer> associations = new HashMap<String, Integer>(); - try (InputStreamReader confReader = new InputStreamReader(xml)) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(confReader); + { + TypedXmlPullParser parser = Xml.resolvePullParser(xml); XmlUtils.beginDocument(parser, "ports"); while (true) { diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index f2eb5af51616..42aad7d7ad5c 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -95,6 +95,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.XmlUtils; import com.android.server.DisplayThread; @@ -146,6 +147,16 @@ public class InputManagerService extends IInputManager.Stub private static final int DEFAULT_VIBRATION_MAGNITUDE = 192; + /** + * We know the issue and are working to fix it, so suppressing the toast to not annoy + * dogfooders. + * + * TODO(b/169067926): Remove this + */ + private static final String[] PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST = { + "com.snapchat.android" // b/173297887 + }; + // Pointer to native input manager service object. private final long mPtr; @@ -2091,6 +2102,10 @@ public class InputManagerService extends IInputManager.Stub // Native callback private void notifyUntrustedTouch(String packageName) { // TODO(b/169067926): Remove toast after gathering feedback on dogfood. + if (ArrayUtils.contains(PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST, packageName)) { + Log.i(TAG, "Suppressing untrusted touch toast for " + packageName); + return; + } DisplayThread.getHandler().post(() -> Toast.makeText(mContext, "Touch obscured by " + packageName diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index f61662d34e75..a735a8f9d305 100644 --- a/services/core/java/com/android/server/input/PersistentDataStore.java +++ b/services/core/java/com/android/server/input/PersistentDataStore.java @@ -253,7 +253,7 @@ final class PersistentDataStore { } } - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, "input-manager-state"); final int outerDepth = parser.getDepth(); @@ -264,7 +264,7 @@ final class PersistentDataStore { } } - private void loadInputDevicesFromXml(XmlPullParser parser) + private void loadInputDevicesFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -285,7 +285,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, "input-manager-state"); @@ -422,7 +422,7 @@ final class PersistentDataStore { return changed; } - public void loadFromXml(XmlPullParser parser) + public void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -505,7 +505,7 @@ final class PersistentDataStore { } } - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(TypedXmlSerializer serializer) throws IOException { for (String layout : mKeyboardLayouts) { serializer.startTag(null, "keyboard-layout"); serializer.attribute(null, "descriptor", layout); diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java index a077b049b014..24b8e340dee9 100644 --- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java +++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java @@ -31,17 +31,13 @@ import android.util.Xml; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; -import com.android.internal.util.FastXmlSerializer; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -158,20 +154,17 @@ final class AdditionalSubtypeUtils { final InputMethodSubtype subtype = subtypesList.get(i); out.startTag(null, NODE_SUBTYPE); if (subtype.hasSubtypeId()) { - out.attribute(null, ATTR_IME_SUBTYPE_ID, - String.valueOf(subtype.getSubtypeId())); + out.attributeInt(null, ATTR_IME_SUBTYPE_ID, subtype.getSubtypeId()); } - out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId())); - out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId())); + out.attributeInt(null, ATTR_ICON, subtype.getIconResId()); + out.attributeInt(null, ATTR_LABEL, subtype.getNameResId()); out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale()); out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG, subtype.getLanguageTag()); out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode()); out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue()); - out.attribute(null, ATTR_IS_AUXILIARY, - String.valueOf(subtype.isAuxiliary() ? 1 : 0)); - out.attribute(null, ATTR_IS_ASCII_CAPABLE, - String.valueOf(subtype.isAsciiCapable() ? 1 : 0)); + out.attributeInt(null, ATTR_IS_AUXILIARY, subtype.isAuxiliary() ? 1 : 0); + out.attributeInt(null, ATTR_IS_ASCII_CAPABLE, subtype.isAsciiCapable() ? 1 : 0); out.endTag(null, NODE_SUBTYPE); } out.endTag(null, NODE_IMI); @@ -243,10 +236,8 @@ final class AdditionalSubtypeUtils { Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId); continue; } - final int icon = Integer.parseInt( - parser.getAttributeValue(null, ATTR_ICON)); - final int label = Integer.parseInt( - parser.getAttributeValue(null, ATTR_LABEL)); + final int icon = parser.getAttributeInt(null, ATTR_ICON); + final int label = parser.getAttributeInt(null, ATTR_LABEL); final String imeSubtypeLocale = parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE); final String languageTag = @@ -269,10 +260,10 @@ final class AdditionalSubtypeUtils { .setSubtypeExtraValue(imeSubtypeExtraValue) .setIsAuxiliary(isAuxiliary) .setIsAsciiCapable(isAsciiCapable); - final String subtypeIdString = - parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID); - if (subtypeIdString != null) { - builder.setSubtypeId(Integer.parseInt(subtypeIdString)); + final int subtypeId = parser.getAttributeInt(null, ATTR_IME_SUBTYPE_ID, + InputMethodSubtype.SUBTYPE_ID_NONE); + if (subtypeId != InputMethodSubtype.SUBTYPE_ID_NONE) { + builder.setSubtypeId(subtypeId); } tempSubtypesArray.add(builder.build()); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0ceaf7748eba..6395094038c7 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -158,6 +158,8 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; +import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.inputmethod.IInputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodDebug; @@ -208,6 +210,7 @@ import java.util.Objects; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; /** * This class provides a system service that manages input methods. @@ -1534,12 +1537,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void sessionCreated(IInputMethodSession session) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated"); final long ident = Binder.clearCallingIdentity(); try { mParentIMMS.onSessionCreated(mMethod, session, mChannel); } finally { Binder.restoreCallingIdentity(ident); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -2284,6 +2289,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurClient == cs) { + hideCurrentInputLocked( + mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); if (mBoundToMethod) { mBoundToMethod = false; if (mCurMethod != null) { @@ -2610,6 +2617,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onServiceConnected(ComponentName name, IBinder service) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected"); synchronized (mMethodMap) { if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { mCurMethod = IInputMethod.Stub.asInterface(service); @@ -2625,6 +2633,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurToken == null) { Slog.w(TAG, "Service connected without a token!"); unbindCurrentMethodLocked(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return; } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); @@ -2638,6 +2647,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } void onSessionCreated(IInputMethod method, IInputMethodSession session, @@ -3345,63 +3355,68 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @NonNull @Override - public InputBindResult startInputOrWindowGainedFocus( + public void startInputOrWindowGainedFocus( @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken, @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode, int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext, - @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) { - if (windowToken == null) { - Slog.e(TAG, "windowToken cannot be null."); - return InputBindResult.NULL; - } - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, - "IMMS.startInputOrWindowGainedFocus"); - ImeTracing.getInstance().triggerManagerServiceDump( - "InputMethodManagerService#startInputOrWindowGainedFocus"); - final int callingUserId = UserHandle.getCallingUserId(); - final int userId; - if (attribute != null && attribute.targetInputMethodUser != null - && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { - mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "Using EditorInfo.targetInputMethodUser requires" - + " INTERACT_ACROSS_USERS_FULL."); - userId = attribute.targetInputMethodUser.getIdentifier(); - if (!mUserManagerInternal.isUserRunning(userId)) { - // There is a chance that we hit here because of race condition. Let's just - // return an error code instead of crashing the caller process, which at least - // has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important - // process. - Slog.e(TAG, "User #" + userId + " is not running."); - return InputBindResult.INVALID_USER; - } - } else { - userId = callingUserId; - } - final InputBindResult result; - synchronized (mMethodMap) { - final long ident = Binder.clearCallingIdentity(); - try { - result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client, - windowToken, startInputFlags, softInputMode, windowFlags, attribute, - inputContext, missingMethods, unverifiedTargetSdkVersion, userId); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - if (result == null) { - // This must never happen, but just in case. - Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" - + InputMethodDebug.startInputReasonToString(startInputReason) - + " windowFlags=#" + Integer.toHexString(windowFlags) - + " editorInfo=" + attribute); + @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion, + IInputBindResultResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> { + if (windowToken == null) { + Slog.e(TAG, "windowToken cannot be null."); return InputBindResult.NULL; } + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, + "IMMS.startInputOrWindowGainedFocus"); + ImeTracing.getInstance().triggerManagerServiceDump( + "InputMethodManagerService#startInputOrWindowGainedFocus"); + final int callingUserId = UserHandle.getCallingUserId(); + final int userId; + if (attribute != null && attribute.targetInputMethodUser != null + && attribute.targetInputMethodUser.getIdentifier() != callingUserId) { + mContext.enforceCallingPermission( + Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "Using EditorInfo.targetInputMethodUser requires" + + " INTERACT_ACROSS_USERS_FULL."); + userId = attribute.targetInputMethodUser.getIdentifier(); + if (!mUserManagerInternal.isUserRunning(userId)) { + // There is a chance that we hit here because of race condition. Let's just + // return an error code instead of crashing the caller process, which at + // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an + // important process. + Slog.e(TAG, "User #" + userId + " is not running."); + return InputBindResult.INVALID_USER; + } + } else { + userId = callingUserId; + } + final InputBindResult result; + synchronized (mMethodMap) { + final long ident = Binder.clearCallingIdentity(); + try { + result = startInputOrWindowGainedFocusInternalLocked(startInputReason, + client, windowToken, startInputFlags, softInputMode, windowFlags, + attribute, inputContext, missingMethods, unverifiedTargetSdkVersion, + userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + if (result == null) { + // This must never happen, but just in case. + Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason=" + + InputMethodDebug.startInputReasonToString(startInputReason) + + " windowFlags=#" + Integer.toHexString(windowFlags) + + " editorInfo=" + attribute); + return InputBindResult.NULL; + } - return result; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - } + return result; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } + }); } @NonNull diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index 62d817c22ae6..6bdae63461b2 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -72,6 +72,8 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.CallbackUtils; +import com.android.internal.inputmethod.IInputBindResultResultCallback; import com.android.internal.inputmethod.IMultiClientInputMethod; import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations; import com.android.internal.inputmethod.IMultiClientInputMethodSession; @@ -104,6 +106,7 @@ import java.lang.annotation.Retention; import java.util.Collections; import java.util.List; import java.util.WeakHashMap; +import java.util.function.Supplier; /** * Actual implementation of multi-client InputMethodManagerService. @@ -1588,7 +1591,26 @@ public final class MultiClientInputMethodManagerService { @BinderThread @Override - public InputBindResult startInputOrWindowGainedFocus( + public void startInputOrWindowGainedFocus( + @StartInputReason int startInputReason, + @Nullable IInputMethodClient client, + @Nullable IBinder windowToken, + @StartInputFlags int startInputFlags, + @SoftInputModeFlags int softInputMode, + int windowFlags, + @Nullable EditorInfo editorInfo, + @Nullable IInputContext inputContext, + @MissingMethodFlags int missingMethods, + int unverifiedTargetSdkVersion, + IInputBindResultResultCallback resultCallback) { + CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> + startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken, + startInputFlags, softInputMode, windowFlags, editorInfo, inputContext, + missingMethods, unverifiedTargetSdkVersion)); + } + + @BinderThread + private InputBindResult startInputOrWindowGainedFocusInternal( @StartInputReason int startInputReason, @Nullable IInputMethodClient client, @Nullable IBinder windowToken, @@ -1676,8 +1698,7 @@ public final class MultiClientInputMethodManagerService { clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( inputContext, missingMethods, editorInfo, startInputFlags, softInputMode, windowHandle); - } catch (RemoteException e) { - } + } catch (RemoteException ignored) { } break; } return InputBindResult.NULL_EDITOR_INFO; @@ -1708,8 +1729,7 @@ public final class MultiClientInputMethodManagerService { clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus( inputContext, missingMethods, editorInfo, startInputFlags, softInputMode, windowHandle); - } catch (RemoteException e) { - } + } catch (RemoteException ignored) { } clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT; return new InputBindResult( InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION, diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java index 28d2e6914103..ab912906617b 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java @@ -17,6 +17,7 @@ package com.android.server.integrity.parser; import android.annotation.Nullable; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.server.integrity.model.RuleMetadata; @@ -26,7 +27,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; /** Helper class for parsing rule metadata. */ public class RuleMetadataParser { @@ -42,8 +42,7 @@ public class RuleMetadataParser { String ruleProvider = ""; String version = ""; - XmlPullParser xmlPullParser = Xml.newPullParser(); - xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name()); + TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream); int eventType; while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) { diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java index 5c51f31ba8cc..7aed35252816 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java +++ b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java @@ -19,6 +19,7 @@ package com.android.server.integrity.serializer; import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG; import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.server.integrity.model.RuleMetadata; @@ -34,8 +35,7 @@ public class RuleMetadataSerializer { /** Serialize the rule metadata to an output stream. */ public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream) throws IOException { - XmlSerializer xmlSerializer = Xml.newSerializer(); - xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name()); + TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream); serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider()); serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion()); @@ -43,8 +43,8 @@ public class RuleMetadataSerializer { xmlSerializer.endDocument(); } - private static void serializeTaggedValue(XmlSerializer xmlSerializer, String tag, String value) - throws IOException { + private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag, + String value) throws IOException { xmlSerializer.startTag(/* namespace= */ null, tag); xmlSerializer.text(value); xmlSerializer.endTag(/* namespace= */ null, tag); diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index a8889fd6b454..9e126673637f 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -987,21 +987,17 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // update client uids updateClientUids(mProviderRequest.getWorkSource()); - mFixInterval = (int) mProviderRequest.getIntervalMillis(); - // check for overflow - if (mFixInterval != mProviderRequest.getIntervalMillis()) { + if (mProviderRequest.getIntervalMillis() <= Integer.MAX_VALUE) { + mFixInterval = (int) mProviderRequest.getIntervalMillis(); + } else { Log.w(TAG, "interval overflow: " + mProviderRequest.getIntervalMillis()); mFixInterval = Integer.MAX_VALUE; } - // requested batch size, or zero to disable batching - int batchSize; - try { - batchSize = mBatchingEnabled ? Math.toIntExact( - mProviderRequest.getMaxUpdateDelayMillis() / mFixInterval) : 0; - } catch (ArithmeticException e) { - batchSize = Integer.MAX_VALUE; - } + // requested batch size, or zero to disable batching + long batchSize = + mBatchingEnabled ? mProviderRequest.getMaxUpdateDelayMillis() / Math.max( + mFixInterval, 1) : 0; if (batchSize < getBatchSize()) { batchSize = 0; } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java index 8a19d62de0b9..0c209c5b48dc 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java @@ -18,7 +18,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; @@ -46,6 +45,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Base64; +import android.util.TypedXmlPullParser; import android.util.Xml; import org.xmlpull.v1.XmlPullParser; @@ -84,8 +84,7 @@ public class KeyChainSnapshotDeserializer { private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(inputStream, OUTPUT_ENCODING); + TypedXmlPullParser parser = Xml.resolvePullParser(inputStream); parser.nextTag(); parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT); @@ -156,7 +155,7 @@ public class KeyChainSnapshotDeserializer { } } - private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser) + private static List<WrappedApplicationKey> readWrappedApplicationKeys(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS); ArrayList<WrappedApplicationKey> keys = new ArrayList<>(); @@ -170,7 +169,7 @@ public class KeyChainSnapshotDeserializer { return keys; } - private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser) + private static WrappedApplicationKey readWrappedApplicationKey(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY); WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder(); @@ -209,7 +208,7 @@ public class KeyChainSnapshotDeserializer { } private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList( - XmlPullParser parser) throws IOException, XmlPullParserException, + TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST); @@ -225,7 +224,7 @@ public class KeyChainSnapshotDeserializer { return keyChainProtectionParamsList; } - private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser) + private static KeyChainProtectionParams readKeyChainProtectionParams(TypedXmlPullParser parser) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS); @@ -269,7 +268,7 @@ public class KeyChainSnapshotDeserializer { } } - private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser) + private static KeyDerivationParams readKeyDerivationParams(TypedXmlPullParser parser) throws XmlPullParserException, IOException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS); @@ -331,7 +330,7 @@ public class KeyChainSnapshotDeserializer { return keyDerivationParams; } - private static int readIntTag(XmlPullParser parser, String tagName) + private static int readIntTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -345,7 +344,7 @@ public class KeyChainSnapshotDeserializer { } } - private static long readLongTag(XmlPullParser parser, String tagName) + private static long readLongTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -359,7 +358,7 @@ public class KeyChainSnapshotDeserializer { } } - private static String readStringTag(XmlPullParser parser, String tagName) + private static String readStringTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -367,7 +366,7 @@ public class KeyChainSnapshotDeserializer { return text; } - private static byte[] readBlobTag(XmlPullParser parser, String tagName) + private static byte[] readBlobTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName); String text = readText(parser); @@ -384,7 +383,7 @@ public class KeyChainSnapshotDeserializer { } } - private static CertPath readCertPathTag(XmlPullParser parser, String tagName) + private static CertPath readCertPathTag(TypedXmlPullParser parser, String tagName) throws IOException, XmlPullParserException, KeyChainSnapshotParserException { byte[] bytes = readBlobTag(parser, tagName); try { @@ -396,7 +395,7 @@ public class KeyChainSnapshotDeserializer { } } - private static String readText(XmlPullParser parser) + private static String readText(TypedXmlPullParser parser) throws IOException, XmlPullParserException { String result = ""; if (parser.next() == XmlPullParser.TEXT) { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java index 8f85a27d4690..6475d9e530f8 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java @@ -22,8 +22,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; class KeyChainSnapshotSchema { static final String NAMESPACE = null; - static final String OUTPUT_ENCODING = "UTF-8"; - static final String CERTIFICATE_FACTORY_TYPE = "X.509"; static final String CERT_PATH_ENCODING = "PkiPath"; diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java index 527e879a198b..eb34e981723d 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java @@ -19,7 +19,6 @@ package com.android.server.locksettings.recoverablekeystore.serialization; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE; -import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS; import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY; @@ -47,10 +46,9 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyDerivationParams; import android.security.keystore.recovery.WrappedApplicationKey; import android.util.Base64; +import android.util.TypedXmlSerializer; import android.util.Xml; -import org.xmlpull.v1.XmlSerializer; - import java.io.IOException; import java.io.OutputStream; import java.security.cert.CertPath; @@ -71,8 +69,7 @@ public class KeyChainSnapshotSerializer { */ public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream) throws IOException, CertificateEncodingException { - XmlSerializer xmlSerializer = Xml.newSerializer(); - xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING); + TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream); xmlSerializer.startDocument( /*encoding=*/ null, /*standalone=*/ null); @@ -87,7 +84,7 @@ public class KeyChainSnapshotSerializer { } private static void writeApplicationKeys( - XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys) + TypedXmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS); for (WrappedApplicationKey key : wrappedApplicationKeys) { @@ -98,15 +95,15 @@ public class KeyChainSnapshotSerializer { xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS); } - private static void writeApplicationKeyProperties( - XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException { + private static void writeApplicationKeyProperties(TypedXmlSerializer xmlSerializer, + WrappedApplicationKey applicationKey) throws IOException { writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias()); writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial()); writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata()); } private static void writeKeyChainProtectionParams( - XmlSerializer xmlSerializer, + TypedXmlSerializer xmlSerializer, List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST); for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) { @@ -118,7 +115,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyChainProtectionParamsProperties( - XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams) + TypedXmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams) throws IOException { writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE, keyChainProtectionParams.getUserSecretType()); @@ -132,7 +129,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyDerivationParams( - XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) + TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) throws IOException { xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS); writeKeyDerivationParamsProperties( @@ -141,7 +138,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyDerivationParamsProperties( - XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) + TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams) throws IOException { writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm()); writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt()); @@ -150,7 +147,7 @@ public class KeyChainSnapshotSerializer { } private static void writeKeyChainSnapshotProperties( - XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot) + TypedXmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot) throws IOException, CertificateEncodingException { writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION, @@ -165,7 +162,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, long propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, long propertyValue) throws IOException { xmlSerializer.startTag(NAMESPACE, propertyName); xmlSerializer.text(Long.toString(propertyValue)); @@ -173,7 +170,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, String propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, String propertyValue) throws IOException { xmlSerializer.startTag(NAMESPACE, propertyName); xmlSerializer.text(propertyValue); @@ -181,7 +178,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue) + TypedXmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue) throws IOException { if (propertyValue == null) { return; @@ -192,7 +189,7 @@ public class KeyChainSnapshotSerializer { } private static void writePropertyTag( - XmlSerializer xmlSerializer, String propertyName, CertPath certPath) + TypedXmlSerializer xmlSerializer, String propertyName, CertPath certPath) throws IOException, CertificateEncodingException { writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING)); } diff --git a/services/core/java/com/android/server/media/MediaKeyDispatcher.java b/services/core/java/com/android/server/media/MediaKeyDispatcher.java index 0dbc8392b610..63618eef250a 100644 --- a/services/core/java/com/android/server/media/MediaKeyDispatcher.java +++ b/services/core/java/com/android/server/media/MediaKeyDispatcher.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; +import android.content.Context; import android.media.session.ISessionManager; import android.media.session.MediaSession; import android.os.Binder; @@ -60,7 +61,7 @@ public abstract class MediaKeyDispatcher { private Map<Integer, Integer> mOverriddenKeyEvents; - public MediaKeyDispatcher() { + public MediaKeyDispatcher(Context context) { // Constructor used for reflection mOverriddenKeyEvents = new HashMap<>(); mOverriddenKeyEvents.put(KeyEvent.KEYCODE_MEDIA_PLAY, 0); diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java index 5fa2b1ceaae9..a4f11b2470f1 100644 --- a/services/core/java/com/android/server/media/MediaServerUtils.java +++ b/services/core/java/com/android/server/media/MediaServerUtils.java @@ -18,6 +18,9 @@ package com.android.server.media; import android.content.Context; import android.content.pm.PackageManager; +import android.media.AudioAttributes; +import android.media.AudioManager; +import android.media.AudioPlaybackConfiguration; import android.os.Binder; import java.io.PrintWriter; @@ -29,7 +32,7 @@ class MediaServerUtils { /** * Verify that caller holds {@link android.Manifest.permission#DUMP}. */ - public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) { + static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) { if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump " + tag + " from from pid=" @@ -40,4 +43,18 @@ class MediaServerUtils { return true; } } + + /** + * Whether the given stream is currently active or not. + */ + static boolean isStreamActive(AudioManager audioManager, int stream) { + for (AudioPlaybackConfiguration configuration + : audioManager.getActivePlaybackConfigurations()) { + AudioAttributes attributes = configuration.getAudioAttributes(); + if (attributes != null && attributes.getVolumeControlStream() == stream) { + return configuration.isActive(); + } + } + return false; + } } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index f9973529a120..ea6e7d7d0bf6 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -23,7 +23,6 @@ import android.content.Intent; import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.AudioManager; -import android.media.AudioSystem; import android.media.MediaMetadata; import android.media.Rating; import android.media.VolumeProvider; @@ -513,7 +512,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR public void run() { try { if (useSuggested) { - if (AudioSystem.isStreamActive(stream, 0)) { + if (MediaServerUtils.isStreamActive(mAudioManager, stream)) { mAudioManager.adjustSuggestedStreamVolumeForUid(stream, direction, flags, opPackageName, uid, pid, mContext.getApplicationInfo().targetSdkVersion); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index c23bfc442bd9..11dbef21b34f 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -43,7 +43,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; -import android.media.AudioSystem; import android.media.IRemoteVolumeControllerCallback; import android.media.Session2Token; import android.media.session.IActiveSessionsListener; @@ -94,7 +93,6 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; /** * System implementation of MediaSessionManager @@ -149,7 +147,6 @@ public class MediaSessionService extends SystemService implements Monitor { private SessionPolicyProvider mCustomSessionPolicyProvider; private MediaKeyDispatcher mCustomMediaKeyDispatcher; - private Map<Integer, Integer> mOverriddenKeyEventsMap; public MediaSessionService(Context context) { super(context); @@ -780,7 +777,6 @@ public class MediaSessionService extends SystemService implements Monitor { private void instantiateCustomDispatcher(String nameFromTesting) { synchronized (mLock) { mCustomMediaKeyDispatcher = null; - mOverriddenKeyEventsMap = null; String customDispatcherClassName = (nameFromTesting == null) ? mContext.getResources().getString(R.string.config_customMediaKeyDispatcher) @@ -788,9 +784,10 @@ public class MediaSessionService extends SystemService implements Monitor { try { if (!TextUtils.isEmpty(customDispatcherClassName)) { Class customDispatcherClass = Class.forName(customDispatcherClassName); - Constructor constructor = customDispatcherClass.getDeclaredConstructor(); - mCustomMediaKeyDispatcher = (MediaKeyDispatcher) constructor.newInstance(); - mOverriddenKeyEventsMap = mCustomMediaKeyDispatcher.getOverriddenKeyEvents(); + Constructor constructor = + customDispatcherClass.getDeclaredConstructor(Context.class); + mCustomMediaKeyDispatcher = + (MediaKeyDispatcher) constructor.newInstance(mContext); } } catch (ClassNotFoundException | InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -810,9 +807,10 @@ public class MediaSessionService extends SystemService implements Monitor { try { if (!TextUtils.isEmpty(customProviderClassName)) { Class customProviderClass = Class.forName(customProviderClassName); - Constructor constructor = customProviderClass.getDeclaredConstructor(); + Constructor constructor = + customProviderClass.getDeclaredConstructor(Context.class); mCustomSessionPolicyProvider = - (SessionPolicyProvider) constructor.newInstance(); + (SessionPolicyProvider) constructor.newInstance(mContext); } } catch (ClassNotFoundException | InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { @@ -2019,7 +2017,7 @@ public class MediaSessionService extends SystemService implements Monitor { boolean preferSuggestedStream = false; if (isValidLocalStreamType(suggestedStream) - && AudioSystem.isStreamActive(suggestedStream, 0)) { + && MediaServerUtils.isStreamActive(mAudioManager, suggestedStream)) { preferSuggestedStream = true; } if (session == null || preferSuggestedStream) { @@ -2028,7 +2026,8 @@ public class MediaSessionService extends SystemService implements Monitor { + ". flags=" + flags + ", preferSuggestedStream=" + preferSuggestedStream + ", session=" + session); } - if (musicOnly && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) { + if (musicOnly && !MediaServerUtils.isStreamActive(mAudioManager, + AudioManager.STREAM_MUSIC)) { if (DEBUG_KEY_EVENT) { Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event," + " flags=" + flags); @@ -2382,9 +2381,12 @@ public class MediaSessionService extends SystemService implements Monitor { return; } - int overriddenKeyEvents = (mCustomMediaKeyDispatcher == null) ? 0 - : mCustomMediaKeyDispatcher.getOverriddenKeyEvents() - .get(keyEvent.getKeyCode()); + int overriddenKeyEvents = 0; + if (mCustomMediaKeyDispatcher != null + && mCustomMediaKeyDispatcher.getOverriddenKeyEvents() != null) { + overriddenKeyEvents = mCustomMediaKeyDispatcher.getOverriddenKeyEvents() + .get(keyEvent.getKeyCode()); + } cancelTrackingIfNeeded(packageName, pid, uid, asSystemService, keyEvent, needWakeLock, opPackageName, stream, musicOnly, overriddenKeyEvents); if (!needTracking(keyEvent, overriddenKeyEvents)) { diff --git a/services/core/java/com/android/server/media/SessionPolicyProvider.java b/services/core/java/com/android/server/media/SessionPolicyProvider.java index 5f02a075344e..332c85adec01 100644 --- a/services/core/java/com/android/server/media/SessionPolicyProvider.java +++ b/services/core/java/com/android/server/media/SessionPolicyProvider.java @@ -18,6 +18,7 @@ package com.android.server.media; import android.annotation.IntDef; import android.annotation.NonNull; +import android.content.Context; import android.media.session.MediaSession; import java.lang.annotation.Retention; @@ -54,7 +55,7 @@ public abstract class SessionPolicyProvider { */ static final int SESSION_POLICY_IGNORE_BUTTON_SESSION = 1 << 1; - public SessionPolicyProvider() { + public SessionPolicyProvider(Context context) { // Constructor used for reflection } diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java index adb98695161a..9c68349af7d5 100644 --- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java +++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java @@ -19,13 +19,17 @@ package com.android.server.media.metrics; import android.content.Context; import android.media.metrics.IPlaybackMetricsManager; import android.media.metrics.PlaybackMetrics; +import android.util.Base64; import com.android.server.SystemService; +import java.security.SecureRandom; + /** * System service manages playback metrics. */ public final class PlaybackMetricsManagerService extends SystemService { + private final SecureRandom mSecureRandom; /** * Initializes the playback metrics manager service. @@ -34,6 +38,7 @@ public final class PlaybackMetricsManagerService extends SystemService { */ public PlaybackMetricsManagerService(Context context) { super(context); + mSecureRandom = new SecureRandom(); } @Override @@ -44,8 +49,16 @@ public final class PlaybackMetricsManagerService extends SystemService { private final class BinderService extends IPlaybackMetricsManager.Stub { @Override - public void reportPlaybackMetrics(PlaybackMetrics metrics, int userId) { + public void reportPlaybackMetrics(String sessionId, PlaybackMetrics metrics, int userId) { // TODO: log it to statsd } + + @Override + public String getSessionId(int userId) { + byte[] byteId = new byte[16]; // 128 bits + mSecureRandom.nextBytes(byteId); + String id = Base64.encodeToString(byteId, Base64.DEFAULT); + return id; + } } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index bdf0fb9cee59..8200ca0523be 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -34,6 +34,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.util.TypedXmlSerializer; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -110,7 +111,7 @@ public class ConditionProviders extends ManagedServices { } @Override - void writeDefaults(XmlSerializer out) throws IOException { + void writeDefaults(TypedXmlSerializer out) throws IOException { synchronized (mDefaultsLock) { String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages); out.attribute(null, ATT_DEFAULTS, defaults); diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index f2e3708051c4..a7ee27286a24 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -60,6 +60,8 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -443,7 +445,7 @@ abstract public class ManagedServices { } } - void writeDefaults(XmlSerializer out) throws IOException { + void writeDefaults(TypedXmlSerializer out) throws IOException { synchronized (mDefaultsLock) { List<String> componentStrings = new ArrayList<>(mDefaultComponents.size()); for (int i = 0; i < mDefaultComponents.size(); i++) { @@ -454,10 +456,10 @@ abstract public class ManagedServices { } } - public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { + public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, getConfig().xmlTag); - out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION)); + out.attributeInt(null, ATT_VERSION, DB_VERSION); writeDefaults(out); @@ -485,8 +487,8 @@ abstract public class ManagedServices { : String.join(ENABLED_SERVICES_SEPARATOR, approved); out.startTag(null, TAG_MANAGED_SERVICES); out.attribute(null, ATT_APPROVED_LIST, allowedItems); - out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); - out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); + out.attributeInt(null, ATT_USER_ID, approvedUserId); + out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary); if (userSet != null) { String userSetItems = String.join(ENABLED_SERVICES_SEPARATOR, userSet); @@ -516,23 +518,23 @@ abstract public class ManagedServices { /** * Writes extra xml attributes to {@link #TAG_MANAGED_SERVICES} tag. */ - protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException {} + protected void writeExtraAttributes(TypedXmlSerializer out, int userId) throws IOException {} /** * Writes extra xml tags within the parent tag specified in {@link Config#xmlTag}. */ - protected void writeExtraXmlTags(XmlSerializer out) throws IOException {} + protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {} /** * This is called to process tags other than {@link #TAG_MANAGED_SERVICES}. */ - protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {} + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {} protected void migrateToXml() { loadAllowedComponentsFromSettings(); } - void readDefaults(XmlPullParser parser) { + void readDefaults(TypedXmlPullParser parser) { String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS); if (!TextUtils.isEmpty(defaultComponents)) { @@ -554,7 +556,7 @@ abstract public class ManagedServices { } public void readXml( - XmlPullParser parser, + TypedXmlPullParser parser, TriPredicate<String, Integer, String> allowedManagedServicePackages, boolean forRestore, int userId) @@ -577,9 +579,9 @@ abstract public class ManagedServices { final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST); // Ignore parser's user id for restore. final int resolvedUserId = forRestore - ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0); + ? userId : parser.getAttributeInt(null, ATT_USER_ID, 0); final boolean isPrimary = - XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true); + parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true); final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET); readExtraAttributes(tag, parser, resolvedUserId); if (allowedManagedServicePackages == null || allowedManagedServicePackages.test( @@ -631,7 +633,7 @@ abstract public class ManagedServices { /** * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag. */ - protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) + protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId) throws IOException {} protected abstract String getRequiredPermission(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b1289dd0e39f..ee860e3ac6d7 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -230,6 +230,8 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.util.proto.ProtoOutputStream; import android.view.accessibility.AccessibilityEvent; @@ -255,7 +257,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.ConcurrentUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.TriPredicate; @@ -284,9 +285,7 @@ import libcore.io.IoUtils; import org.json.JSONException; import org.json.JSONObject; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -735,7 +734,7 @@ public class NotificationManagerService extends SystemService { null, MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId); if (candidate != null && validAssistants.contains(candidate)) { - setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true); + setNotificationAssistantAccessGrantedForUserInternal(candidate, userId, true, false); return true; } return false; @@ -743,8 +742,13 @@ public class NotificationManagerService extends SystemService { void readPolicyXml(InputStream stream, boolean forRestore, int userId) throws XmlPullParserException, NumberFormatException, IOException { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlPullParser parser; + if (forRestore) { + parser = Xml.newFastPullParser(); + parser.setInput(stream, StandardCharsets.UTF_8.name()); + } else { + parser = Xml.resolvePullParser(stream); + } XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY); boolean migratedManagedServices = false; boolean ineligibleForManagedServices = forRestore && mUm.isManagedProfile(userId); @@ -781,9 +785,8 @@ public class NotificationManagerService extends SystemService { if (forRestore && userId != UserHandle.USER_SYSTEM) { continue; } - mLockScreenAllowSecureNotifications = - safeBoolean(parser.getAttributeValue(null, - LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE), true); + mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null, + LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true); } } @@ -856,11 +859,16 @@ public class NotificationManagerService extends SystemService { private void writePolicyXml(OutputStream stream, boolean forBackup, int userId) throws IOException { - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer out; + if (forBackup) { + out = Xml.newFastSerializer(); + out.setOutput(stream, StandardCharsets.UTF_8.name()); + } else { + out = Xml.resolveSerializer(stream); + } out.startDocument(null, true); out.startTag(null, TAG_NOTIFICATION_POLICY); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); + out.attributeInt(null, ATTR_VERSION, DB_VERSION); mZenModeHelper.writeXml(out, forBackup, null, userId); mPreferencesHelper.writeXml(out, forBackup, userId); mListeners.writeXml(out, forBackup, userId); @@ -4902,7 +4910,8 @@ public class NotificationManagerService extends SystemService { } final long identity = Binder.clearCallingIdentity(); try { - setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); + setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted, + true); } finally { Binder.restoreCallingIdentity(identity); } @@ -5153,7 +5162,7 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int baseUserId, boolean granted) { + ComponentName assistant, int baseUserId, boolean granted, boolean userSet) { List<UserInfo> users = mUm.getEnabledProfiles(baseUserId); if (users != null) { for (UserInfo user : users) { @@ -5163,7 +5172,7 @@ public class NotificationManagerService extends SystemService { mAssistants.getAllowedComponents(userId)); if (allowedAssistant != null) { setNotificationAssistantAccessGrantedForUserInternal( - allowedAssistant, userId, false); + allowedAssistant, userId, false, userSet); } continue; } @@ -5172,7 +5181,7 @@ public class NotificationManagerService extends SystemService { mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(), userId, false, granted); mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(), - userId, true, granted); + userId, true, granted, userSet); getContext().sendBroadcastAsUser( new Intent(ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) @@ -8975,7 +8984,7 @@ public class NotificationManagerService extends SystemService { } @Override - protected void writeExtraXmlTags(XmlSerializer out) throws IOException { + protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException { synchronized (mLock) { out.startTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES); out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustments)); @@ -8984,7 +8993,7 @@ public class NotificationManagerService extends SystemService { } @Override - protected void readExtraTag(String tag, XmlPullParser parser) throws IOException { + protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException { if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) { final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES); synchronized (mLock) { @@ -9104,9 +9113,9 @@ public class NotificationManagerService extends SystemService { } @Override - protected void readExtraAttributes(String tag, XmlPullParser parser, int userId) + protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId) throws IOException { - boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false); + boolean userSet = parser.getAttributeBoolean(null, ATT_USER_SET, false); setUserSet(userId, userSet); } @@ -9327,7 +9336,7 @@ public class NotificationManagerService extends SystemService { @Override protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId, - boolean isPrimary, boolean enabled) { + boolean isPrimary, boolean enabled, boolean userSet) { // Ensures that only one component is enabled at a time if (enabled) { List<ComponentName> allowedComponents = getAllowedComponents(userId); @@ -9335,10 +9344,10 @@ public class NotificationManagerService extends SystemService { ComponentName currentComponent = CollectionUtils.firstOrNull(allowedComponents); if (currentComponent.flattenToString().equals(pkgOrComponent)) return; setNotificationAssistantAccessGrantedForUserInternal( - currentComponent, userId, false); + currentComponent, userId, false, userSet); } } - super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled); + super.setPackageOrComponentEnabled(pkgOrComponent, userId, isPrimary, enabled, userSet); } private boolean isVerboseLogEnabled() { @@ -10106,18 +10115,13 @@ public class NotificationManagerService extends SystemService { } } - private void writeSecureNotificationsPolicy(XmlSerializer out) throws IOException { + private void writeSecureNotificationsPolicy(TypedXmlSerializer out) throws IOException { out.startTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG); - out.attribute(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, - Boolean.toString(mLockScreenAllowSecureNotifications)); + out.attributeBoolean(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, + mLockScreenAllowSecureNotifications); out.endTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG); } - private static boolean safeBoolean(String val, boolean defValue) { - if (TextUtils.isEmpty(val)) return defValue; - return Boolean.parseBoolean(val); - } - /** * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too * aggressive and annoying the user. diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 2f990c62305f..1c0349d1b51f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -56,6 +56,8 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -195,21 +197,15 @@ public class PreferencesHelper implements RankingConfig { syncChannelsBypassingDnd(mContext.getUserId()); } - public void readXml(XmlPullParser parser, boolean forRestore, int userId) + public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return; String tag = parser.getName(); if (!TAG_RANKING.equals(tag)) return; - boolean upgradeForBubbles = false; - if (parser.getAttributeCount() > 0) { - String attribute = parser.getAttributeName(0); - if (ATT_VERSION.equals(attribute)) { - int xmlVersion = Integer.parseInt(parser.getAttributeValue(0)); - upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; - } - } + final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1); + boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; synchronized (mPackagePreferences) { while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); @@ -221,10 +217,10 @@ public class PreferencesHelper implements RankingConfig { if (forRestore && userId != UserHandle.USER_SYSTEM) { continue; } - mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute( - parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); + mHideSilentStatusBarIcons = parser.getAttributeBoolean(null, + ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS); } else if (TAG_PACKAGE.equals(tag)) { - int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + int uid = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); String name = parser.getAttributeValue(null, ATT_NAME); if (!TextUtils.isEmpty(name)) { if (forRestore) { @@ -243,36 +239,36 @@ public class PreferencesHelper implements RankingConfig { } int bubblePref = hasSAWPermission ? BUBBLE_PREFERENCE_ALL - : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE, - DEFAULT_BUBBLE_PREFERENCE); + : parser.getAttributeInt( + null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE); PackagePreferences r = getOrCreatePackagePreferencesLocked( name, userId, uid, - XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), - XmlUtils.readIntAttribute(parser, ATT_PRIORITY, - DEFAULT_PRIORITY), - XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY), - XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), + parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE), + parser.getAttributeInt( + null, ATT_PRIORITY, DEFAULT_PRIORITY), + parser.getAttributeInt( + null, ATT_VISIBILITY, DEFAULT_VISIBILITY), + parser.getAttributeBoolean( + null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE), bubblePref); - r.importance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); - r.priority = XmlUtils.readIntAttribute( - parser, ATT_PRIORITY, DEFAULT_PRIORITY); - r.visibility = XmlUtils.readIntAttribute( - parser, ATT_VISIBILITY, DEFAULT_VISIBILITY); - r.showBadge = XmlUtils.readBooleanAttribute( - parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); - r.lockedAppFields = XmlUtils.readIntAttribute(parser, - ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); - r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute( - parser, ATT_SENT_INVALID_MESSAGE, false); - r.hasSentValidMessage = XmlUtils.readBooleanAttribute( - parser, ATT_SENT_VALID_MESSAGE, false); - r.userDemotedMsgApp = XmlUtils.readBooleanAttribute( - parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false); + r.importance = parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + r.priority = parser.getAttributeInt( + null, ATT_PRIORITY, DEFAULT_PRIORITY); + r.visibility = parser.getAttributeInt( + null, ATT_VISIBILITY, DEFAULT_VISIBILITY); + r.showBadge = parser.getAttributeBoolean( + null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE); + r.lockedAppFields = parser.getAttributeInt( + null, ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS); + r.hasSentInvalidMessage = parser.getAttributeBoolean( + null, ATT_SENT_INVALID_MESSAGE, false); + r.hasSentValidMessage = parser.getAttributeBoolean( + null, ATT_SENT_VALID_MESSAGE, false); + r.userDemotedMsgApp = parser.getAttributeBoolean( + null, ATT_USER_DEMOTED_INVALID_MSG_APP, false); final int innerDepth = parser.getDepth(); while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -307,8 +303,8 @@ public class PreferencesHelper implements RankingConfig { } String id = parser.getAttributeValue(null, ATT_ID); String channelName = parser.getAttributeValue(null, ATT_NAME); - int channelImportance = XmlUtils.readIntAttribute( - parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); + int channelImportance = parser.getAttributeInt( + null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE); if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) { NotificationChannel channel = new NotificationChannel(id, channelName, channelImportance); @@ -338,14 +334,13 @@ public class PreferencesHelper implements RankingConfig { // Delegate if (TAG_DELEGATE.equals(tagName)) { int delegateId = - XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID); + parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID); String delegateName = XmlUtils.readStringAttribute(parser, ATT_NAME); - boolean delegateEnabled = XmlUtils.readBooleanAttribute( - parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED); - boolean userAllowed = XmlUtils.readBooleanAttribute( - parser, ATT_USER_ALLOWED, - Delegate.DEFAULT_USER_ALLOWED); + boolean delegateEnabled = parser.getAttributeBoolean( + null, ATT_ENABLED, Delegate.DEFAULT_ENABLED); + boolean userAllowed = parser.getAttributeBoolean( + null, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED); Delegate d = null; if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty( delegateName)) { @@ -502,13 +497,13 @@ public class PreferencesHelper implements RankingConfig { return true; } - public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException { + public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException { out.startTag(null, TAG_RANKING); - out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION)); + out.attributeInt(null, ATT_VERSION, XML_VERSION); if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS && (!forBackup || userId == UserHandle.USER_SYSTEM)) { out.startTag(null, TAG_STATUS_ICONS); - out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons)); + out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons); out.endTag(null, TAG_STATUS_ICONS); } @@ -536,42 +531,41 @@ public class PreferencesHelper implements RankingConfig { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATT_NAME, r.pkg); if (r.importance != DEFAULT_IMPORTANCE) { - out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance)); + out.attributeInt(null, ATT_IMPORTANCE, r.importance); } if (r.priority != DEFAULT_PRIORITY) { - out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority)); + out.attributeInt(null, ATT_PRIORITY, r.priority); } if (r.visibility != DEFAULT_VISIBILITY) { - out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility)); + out.attributeInt(null, ATT_VISIBILITY, r.visibility); } if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) { - out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference)); + out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference); } - out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge)); - out.attribute(null, ATT_APP_USER_LOCKED_FIELDS, - Integer.toString(r.lockedAppFields)); - out.attribute(null, ATT_SENT_INVALID_MESSAGE, - Boolean.toString(r.hasSentInvalidMessage)); - out.attribute(null, ATT_SENT_VALID_MESSAGE, - Boolean.toString(r.hasSentValidMessage)); - out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP, - Boolean.toString(r.userDemotedMsgApp)); + out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge); + out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS, + r.lockedAppFields); + out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE, + r.hasSentInvalidMessage); + out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE, + r.hasSentValidMessage); + out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP, + r.userDemotedMsgApp); if (!forBackup) { - out.attribute(null, ATT_UID, Integer.toString(r.uid)); + out.attributeInt(null, ATT_UID, r.uid); } if (r.delegate != null) { out.startTag(null, TAG_DELEGATE); out.attribute(null, ATT_NAME, r.delegate.mPkg); - out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid)); + out.attributeInt(null, ATT_UID, r.delegate.mUid); if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) { - out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled)); + out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled); } if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) { - out.attribute(null, ATT_USER_ALLOWED, - Boolean.toString(r.delegate.mUserAllowed)); + out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed); } out.endTag(null, TAG_DELEGATE); } diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index f7d69fdc09d2..b5ca2abf8afe 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -32,6 +32,8 @@ import android.util.ArrayMap; import android.util.IntArray; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -57,7 +59,7 @@ import java.util.Set; * NotificationManagerService helper for handling snoozed notifications. */ public class SnoozeHelper { - public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1"; + public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1; protected static final String XML_TAG_NAME = "snoozed-notifications"; @@ -547,7 +549,7 @@ public class SnoozeHelper { } } - protected void writeXml(XmlSerializer out) throws IOException { + protected void writeXml(TypedXmlSerializer out) throws IOException { synchronized (mLock) { final long currentTime = System.currentTimeMillis(); out.startTag(null, XML_TAG_NAME); @@ -573,7 +575,7 @@ public class SnoozeHelper { void insert(T t) throws IOException; } - private <T> void writeXml(XmlSerializer out, + private <T> void writeXml(TypedXmlSerializer out, ArrayMap<String, ArrayMap<String, T>> targets, String tag, Inserter<T> attributeInserter) throws IOException { @@ -596,21 +598,18 @@ public class SnoozeHelper { attributeInserter.insert(value); - out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, + out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, XML_SNOOZED_NOTIFICATION_VERSION); out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - - out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); - out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID, - String.valueOf(userId)); + out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId); out.endTag(null, tag); } } } - protected void readXml(XmlPullParser parser, long currentTime) + protected void readXml(TypedXmlPullParser parser, long currentTime) throws XmlPullParserException, IOException { int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -622,16 +621,16 @@ public class SnoozeHelper { if (type == XmlPullParser.START_TAG && (XML_SNOOZED_NOTIFICATION.equals(tag) || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) - && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL) - .equals(XML_SNOOZED_NOTIFICATION_VERSION)) { + && parser.getAttributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, -1) + == XML_SNOOZED_NOTIFICATION_VERSION) { try { final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY); final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG); - final int userId = XmlUtils.readIntAttribute( - parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); + final int userId = parser.getAttributeInt( + null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); if (tag.equals(XML_SNOOZED_NOTIFICATION)) { - final Long time = XmlUtils.readLongAttribute( - parser, XML_SNOOZED_NOTIFICATION_TIME, 0); + final Long time = parser.getAttributeLong( + null, XML_SNOOZED_NOTIFICATION_TIME, 0); if (time > currentTime) { //only read new stuff synchronized (mLock) { storeRecordLocked( diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 13cd6e547629..94f46ba6bc60 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -72,6 +72,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -702,7 +704,7 @@ public class ZenModeHelper { } } - public void readXml(XmlPullParser parser, boolean forRestore, int userId) + public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) throws XmlPullParserException, IOException { ZenModeConfig config = ZenModeConfig.readXml(parser); String reason = "readXml"; @@ -761,7 +763,7 @@ public class ZenModeHelper { } } - public void writeXml(XmlSerializer out, boolean forBackup, Integer version, int userId) + public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId) throws IOException { synchronized (mConfigs) { final int n = mConfigs.size(); diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java index 8a2d823cf7e9..0613dff31da5 100644 --- a/services/core/java/com/android/server/om/OverlayManagerSettings.java +++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java @@ -411,7 +411,7 @@ final class OverlayManagerSettings { table.clear(); final TypedXmlPullParser parser = Xml.resolvePullParser(is); XmlUtils.beginDocument(parser, TAG_OVERLAYS); - int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); + int version = parser.getAttributeInt(null, ATTR_VERSION); if (version != CURRENT_VERSION) { upgrade(version); } @@ -445,19 +445,19 @@ final class OverlayManagerSettings { } } - private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth) - throws IOException { + private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser, + final int depth) throws IOException, XmlPullParserException { final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME); - final int userId = XmlUtils.readIntAttribute(parser, ATTR_USER_ID); + final int userId = parser.getAttributeInt(null, ATTR_USER_ID); final String targetPackageName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_PACKAGE_NAME); final String targetOverlayableName = XmlUtils.readStringAttribute(parser, ATTR_TARGET_OVERLAYABLE_NAME); final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH); - final int state = XmlUtils.readIntAttribute(parser, ATTR_STATE); - final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED); - final boolean isStatic = XmlUtils.readBooleanAttribute(parser, ATTR_IS_STATIC); - final int priority = XmlUtils.readIntAttribute(parser, ATTR_PRIORITY); + final int state = parser.getAttributeInt(null, ATTR_STATE); + final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false); + final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false); + final int priority = parser.getAttributeInt(null, ATTR_PRIORITY); final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY); return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName, @@ -470,7 +470,7 @@ final class OverlayManagerSettings { xml.startDocument(null, true); xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); xml.startTag(null, TAG_OVERLAYS); - XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION); + xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION); final int n = table.size(); for (int i = 0; i < n; i++) { @@ -485,15 +485,15 @@ final class OverlayManagerSettings { @NonNull final SettingsItem item) throws IOException { xml.startTag(null, TAG_ITEM); XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName); - XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId); + xml.attributeInt(null, ATTR_USER_ID, item.mUserId); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName); XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME, item.mTargetOverlayableName); XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath); - XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState); + xml.attributeInt(null, ATTR_STATE, item.mState); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled); XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable); - XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority); + xml.attributeInt(null, ATTR_PRIORITY, item.mPriority); XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory); xml.endTag(null, TAG_ITEM); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index db5eb844f1b4..85659edd1321 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; import static android.Manifest.permission.REQUEST_DELETE_PACKAGES; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; -import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; @@ -44,7 +43,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; -import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; @@ -343,7 +341,6 @@ import com.android.internal.content.PackageHelper; import com.android.internal.content.om.OverlayConfig; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.SomeArgs; -import com.android.internal.os.Zygote; import com.android.internal.telephony.CarrierAppUtils; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -1744,6 +1741,14 @@ public class PackageManagerService extends IPackageManager.Stub private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); + /** + * Invalidate the package info cache, which includes updating the cached computer. + * @hide + */ + public static void invalidatePackageInfoCache() { + PackageManager.invalidatePackageInfoCache(); + } + class PackageHandler extends Handler { PackageHandler(Looper looper) { @@ -2172,7 +2177,7 @@ public class PackageManagerService extends IPackageManager.Stub private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, - String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, + String[] grantedPermissions, List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver, int dataLoaderType) { @@ -2205,31 +2210,22 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } - // Allowlist any restricted permissions first as some may be runtime - // that the installer requested to be granted at install time. - if (whitelistedRestrictedPermissions != null - && !whitelistedRestrictedPermissions.isEmpty()) { - mPermissionManager.setAllowlistedRestrictedPermissions(res.pkg, - whitelistedRestrictedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - res.newUsers); + final List<String> grantedPermissionsList; + if (grantPermissions) { + if (grantedPermissions != null) { + grantedPermissionsList = Arrays.asList(grantedPermissions); + } else { + grantedPermissionsList = res.pkg.getRequestedPermissions(); + } + } else { + grantedPermissionsList = Collections.emptyList(); } - - if (autoRevokePermissionsMode == MODE_ALLOWED - || autoRevokePermissionsMode == MODE_IGNORED) { - mPermissionManager.setAutoRevokeExempted(res.pkg, - autoRevokePermissionsMode == MODE_IGNORED, res.newUsers); + if (allowlistedRestrictedPermissions == null) { + allowlistedRestrictedPermissions = Collections.emptyList(); } - - // Now that we successfully installed the package, grant runtime - // permissions if requested before broadcasting the install. Also - // for legacy apps in permission review mode we clear the permission - // review flag which is used to emulate runtime permissions for - // legacy apps. - if (grantPermissions) { - final int callingUid = Binder.getCallingUid(); - mPermissionManager.grantRequestedRuntimePermissions(res.pkg, - grantedPermissions != null ? Arrays.asList(grantedPermissions) : null, - res.newUsers); + for (final int userId : res.newUsers) { + mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList, + allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId); } final String installerPackageName = @@ -2695,14 +2691,14 @@ public class PackageManagerService extends IPackageManager.Stub // We normally invalidate when we write settings, but in cases where we delay and // coalesce settings writes, this strategy would have us invalidate the cache too late. // Invalidating on schedule addresses this problem. - PackageManager.invalidatePackageInfoCache(); + invalidatePackageInfoCache(); if (!mHandler.hasMessages(WRITE_SETTINGS)) { mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY); } } void scheduleWritePackageListLocked(int userId) { - PackageManager.invalidatePackageInfoCache(); + invalidatePackageInfoCache(); if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) { Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST); msg.arg1 = userId; @@ -2716,7 +2712,7 @@ public class PackageManagerService extends IPackageManager.Stub } void scheduleWritePackageRestrictionsLocked(int userId) { - PackageManager.invalidatePackageInfoCache(); + invalidatePackageInfoCache(); final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManager.getUserIds() : new int[]{userId}; for (int nextUserId : userIds) { @@ -4105,10 +4101,7 @@ public class PackageManagerService extends IPackageManager.Stub // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" : injector.getSystemWrapper().digestOfProperties( - "ro.build.fingerprint", - StorageManager.PROP_ISOLATED_STORAGE, - StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT - ); + "ro.build.fingerprint"); // Reconcile cache directories, keeping only what we'd actually use. for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) { @@ -6954,8 +6947,7 @@ public class PackageManagerService extends IPackageManager.Stub private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId, String resolvedType, int flags) { - PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities - .get(userId); + PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId); //TODO(b/158003772): Remove double query List<PersistentPreferredActivity> pprefs = ppir != null ? ppir.queryIntent(intent, resolvedType, @@ -6974,8 +6966,7 @@ public class PackageManagerService extends IPackageManager.Stub private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean debug, int userId) { final int N = query.size(); - PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities - .get(userId); + PersistentPreferredIntentResolver ppir = mSettings.getPersistentPreferredActivities(userId); // Get the list of persistent preferred activities that handle the intent if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for presistent preferred activities..."); List<PersistentPreferredActivity> pprefs = ppir != null @@ -7075,7 +7066,7 @@ public class PackageManagerService extends IPackageManager.Stub return pri; } - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + PreferredIntentResolver pir = mSettings.getPreferredActivities(userId); // Get the list of preferred activities that handle the intent if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities..."); List<PreferredActivity> prefs = pir != null @@ -7302,7 +7293,7 @@ public class PackageManagerService extends IPackageManager.Stub private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); + CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolvers(userId); if (resolver != null) { return resolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } @@ -9904,7 +9895,7 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.INTERACT_ACROSS_PROFILES, PermissionChecker.PID_UNKNOWN, callingUid, - mPmInternal.getPackage(callingUid).getPackageName()) + getPackage(callingUid).getPackageName()) == PermissionChecker.PERMISSION_GRANTED) { return; } @@ -13611,11 +13602,12 @@ public class PackageManagerService extends IPackageManager.Stub int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId, @PackageManager.InstallFlags int installFlags, @PackageManager.InstallReason int installReason, - @Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) { + @Nullable List<String> allowlistedRestrictedPermissions, + @Nullable IntentSender intentSender) { if (DEBUG_INSTALL) { Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + " installFlags=" + installFlags + " installReason=" + installReason - + " whiteListedPermissions=" + whiteListedPermissions); + + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions); } final int callingUid = Binder.getCallingUid(); @@ -13681,11 +13673,12 @@ public class PackageManagerService extends IPackageManager.Stub if (pkgSetting.pkg != null) { if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { - whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions(); + allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions(); + } else if (allowlistedRestrictedPermissions == null) { + allowlistedRestrictedPermissions = Collections.emptyList(); } - mPermissionManager.setAllowlistedRestrictedPermissions(pkgSetting.pkg, - whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - new int[] { userId }); + mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(), + allowlistedRestrictedPermissions, MODE_DEFAULT, userId); } if (pkgSetting.pkg != null) { @@ -20460,7 +20453,7 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + final PreferredIntentResolver pir = mSettings.getPreferredActivities(userId); if (pir != null) { // Get all of the existing entries that exactly match this filter. final ArrayList<PreferredActivity> existing = pir.findFilters(filter); @@ -20557,35 +20550,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private void clearPackagePreferredActivitiesLPw(String packageName, @NonNull SparseBooleanArray outUserChanged, int userId) { - ArrayList<PreferredActivity> removed = null; - for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { - final int thisUserId = mSettings.mPreferredActivities.keyAt(i); - PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); - if (userId != UserHandle.USER_ALL && userId != thisUserId) { - continue; - } - Iterator<PreferredActivity> it = pir.filterIterator(); - while (it.hasNext()) { - PreferredActivity pa = it.next(); - // Mark entry for removal only if it matches the package name - // and the entry is of type "always". - if (packageName == null || - (pa.mPref.mComponent.getPackageName().equals(packageName) - && pa.mPref.mAlways)) { - if (removed == null) { - removed = new ArrayList<>(); - } - removed.add(pa); - } - } - if (removed != null) { - for (int j=0; j<removed.size(); j++) { - PreferredActivity pa = removed.get(j); - pir.removeFilter(pa); - } - outUserChanged.put(thisUserId, true); - } - } + mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId); } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ @@ -20702,7 +20667,7 @@ public class PackageManagerService extends IPackageManager.Stub final int userId = UserHandle.getCallingUserId(); // reader synchronized (mLock) { - PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); + PreferredIntentResolver pir = mSettings.getPreferredActivities(userId); if (pir != null) { final Iterator<PreferredActivity> it = pir.filterIterator(); while (it.hasNext()) { @@ -20757,35 +20722,9 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException( "clearPackagePersistentPreferredActivities can only be run by the system"); } - ArrayList<PersistentPreferredActivity> removed = null; boolean changed = false; synchronized (mLock) { - for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) { - final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i); - PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities - .valueAt(i); - if (userId != thisUserId) { - continue; - } - Iterator<PersistentPreferredActivity> it = ppir.filterIterator(); - while (it.hasNext()) { - PersistentPreferredActivity ppa = it.next(); - // Mark entry for removal only if it matches the package name. - if (ppa.mComponent.getPackageName().equals(packageName)) { - if (removed == null) { - removed = new ArrayList<>(); - } - removed.add(ppa); - } - } - if (removed != null) { - for (int j=0; j<removed.size(); j++) { - PersistentPreferredActivity ppa = removed.get(j); - ppir.removeFilter(ppa); - } - changed = true; - } - } + changed = mSettings.clearPackagePersistentPreferredActivities(packageName, userId); } if (changed) { updateDefaultHomeNotLocked(userId); @@ -22165,33 +22104,9 @@ public class PackageManagerService extends IPackageManager.Stub } synchronized (mLock) { - // Verify that all of the preferred activity components actually - // exist. It is possible for applications to be updated and at - // that point remove a previously declared activity component that - // had been set as a preferred activity. We try to clean this up - // the next time we encounter that preferred activity, but it is - // possible for the user flow to never be able to return to that - // situation so here we do a validity check to make sure we haven't - // left any junk around. - ArrayList<PreferredActivity> removed = new ArrayList<>(); - for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { - PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); - removed.clear(); - for (PreferredActivity pa : pir.filterSet()) { - if (!mComponentResolver.isActivityDefined(pa.mPref.mComponent)) { - removed.add(pa); - } - } - if (removed.size() > 0) { - for (int r=0; r<removed.size(); r++) { - PreferredActivity pa = removed.get(r); - Slog.w(TAG, "Removing dangling preferred activity: " - + pa.mPref.mComponent); - pir.removeFilter(pa); - } - mSettings.writePackageRestrictionsLPr( - mSettings.mPreferredActivities.keyAt(i)); - } + ArrayList<Integer> changed = mSettings.systemReady(mComponentResolver); + for (int i = 0; i < changed.size(); i++) { + mSettings.writePackageRestrictionsLPr(changed.get(i)); } } @@ -22221,22 +22136,6 @@ public class PackageManagerService extends IPackageManager.Stub mInstallerService.systemReady(); mPackageDexOptimizer.systemReady(); - mInjector.getLocalService(StorageManagerInternal.class).addExternalStoragePolicy( - new StorageManagerInternal.ExternalStorageMountPolicy() { - @Override - public int getMountMode(int uid, String packageName) { - if (Process.isIsolated(uid)) { - return Zygote.MOUNT_EXTERNAL_NONE; - } - return Zygote.MOUNT_EXTERNAL_DEFAULT; - } - - @Override - public boolean hasExternalStorage(int uid, String packageName) { - return true; - } - }); - // Now that we're mostly running, clean up stale users and apps mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL); reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL); @@ -22718,17 +22617,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED)) { - for (int i=0; i<mSettings.mPreferredActivities.size(); i++) { - PreferredIntentResolver pir = mSettings.mPreferredActivities.valueAt(i); - int user = mSettings.mPreferredActivities.keyAt(i); - if (pir.dump(pw, - dumpState.getTitlePrinted() - ? "\nPreferred Activities User " + user + ":" - : "Preferred Activities User " + user + ":", " ", - packageName, true, false)) { - dumpState.setTitlePrinted(true); - } - } + mSettings.dumpPreferred(pw, dumpState, packageName); } if (!checkin && dumpState.isDumping(DumpState.DUMP_PREFERRED_XML)) { @@ -24827,6 +24716,26 @@ public class PackageManagerService extends IPackageManager.Stub } } + private AndroidPackage getPackage(String packageName) { + synchronized (mLock) { + packageName = resolveInternalPackageNameLPr( + packageName, PackageManager.VERSION_CODE_HIGHEST); + return mPackages.get(packageName); + } + } + + private AndroidPackage getPackage(int uid) { + synchronized (mLock) { + final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID); + AndroidPackage pkg = null; + final int numPackages = packageNames == null ? 0 : packageNames.length; + for (int i = 0; pkg == null && i < numPackages; i++) { + pkg = mPackages.get(packageNames[i]); + } + return pkg; + } + } + private class PackageManagerInternalImpl extends PackageManagerInternal { @Override public List<ApplicationInfo> getInstalledApplications(int flags, int userId, @@ -24937,24 +24846,12 @@ public class PackageManagerService extends IPackageManager.Stub @Override public AndroidPackage getPackage(String packageName) { - synchronized (mLock) { - packageName = resolveInternalPackageNameLPr( - packageName, PackageManager.VERSION_CODE_HIGHEST); - return mPackages.get(packageName); - } + return PackageManagerService.this.getPackage(packageName); } @Override public AndroidPackage getPackage(int uid) { - synchronized (mLock) { - final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID); - AndroidPackage pkg = null; - final int numPackages = packageNames == null ? 0 : packageNames.length; - for (int i = 0; pkg == null && i < numPackages; i++) { - pkg = mPackages.get(packageNames[i]); - } - return pkg; - } + return PackageManagerService.this.getPackage(uid); } @Nullable @@ -25561,7 +25458,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - PackageManager.invalidatePackageInfoCache(); + invalidatePackageInfoCache(); return true; } diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java index d18a02d9f315..ee9ed3b90599 100644 --- a/services/core/java/com/android/server/pm/PackageProperty.java +++ b/services/core/java/com/android/server/pm/PackageProperty.java @@ -274,7 +274,7 @@ public class PackageProperty { private Property getApplicationProperty(String propertyName, String packageName) { final ArrayMap<String, ArrayList<Property>> packagePropertyMap = - mApplicationProperties.get(propertyName); + mApplicationProperties != null ? mApplicationProperties.get(propertyName) : null; if (packagePropertyMap == null) { return null; } diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java index 804faa1b7f0f..ff6b73b36f62 100644 --- a/services/core/java/com/android/server/pm/PreferredComponent.java +++ b/services/core/java/com/android/server/pm/PreferredComponent.java @@ -252,6 +252,37 @@ public class PreferredComponent { return numMatch == NS; } + public boolean sameSet(PreferredComponent pc) { + if (mSetPackages == null || pc == null || pc.mSetPackages == null + || !sameComponent(pc.mComponent)) { + return false; + } + final int otherPackageCount = pc.mSetPackages.length; + final int packageCount = mSetPackages.length; + if (otherPackageCount != packageCount) { + return false; + } + for (int i = 0; i < packageCount; i++) { + if (!mSetPackages[i].equals(pc.mSetPackages[i]) + || !mSetClasses[i].equals(pc.mSetClasses[i])) { + return false; + } + } + return true; + } + + /** Returns true if the preferred component represents the provided ComponentName. */ + private boolean sameComponent(ComponentName comp) { + if (mComponent == null || comp == null) { + return false; + } + if (mComponent.getPackageName().equals(comp.getPackageName()) + && mComponent.getClassName().equals(comp.getClassName())) { + return true; + } + return false; + } + public boolean isSuperset(List<ResolveInfo> query, boolean excludeSetupWizardPackage) { if (mSetPackages == null) { return query == null; diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java index a261e29b05a7..ff3df130a3cc 100644 --- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java +++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java @@ -22,6 +22,7 @@ import android.content.IntentFilter; import java.io.PrintWriter; import com.android.server.IntentResolver; +import java.util.ArrayList; public class PreferredIntentResolver extends IntentResolver<PreferredActivity, PreferredActivity> { @@ -45,4 +46,24 @@ public class PreferredIntentResolver protected IntentFilter getIntentFilter(@NonNull PreferredActivity input) { return input; } + + public boolean shouldAddPreferredActivity(PreferredActivity pa) { + ArrayList<PreferredActivity> pal = findFilters(pa); + if (pal == null || pal.isEmpty()) { + return true; + } + if (!pa.mPref.mAlways) { + return false; + } + final int activityCount = pal.size(); + for (int i = 0; i < activityCount; i++) { + PreferredActivity cur = pal.get(i); + if (cur.mPref.mAlways + && cur.mPref.mMatch == (pa.mPref.mMatch & IntentFilter.MATCH_CATEGORY_MASK) + && cur.mPref.sameSet(pa.mPref)) { + return false; + } + } + return true; + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 966090cb96a7..f47b4b46fdd4 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -369,16 +369,16 @@ public final class Settings { // The user's preferred activities associated with particular intent // filters. - final SparseArray<PreferredIntentResolver> mPreferredActivities = + private final SparseArray<PreferredIntentResolver> mPreferredActivities = new SparseArray<PreferredIntentResolver>(); // The persistent preferred activities of the user's profile/device owner // associated with particular intent filters. - final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities = + private final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities = new SparseArray<PersistentPreferredIntentResolver>(); // For every user, it is used to find to which other users the intent can be forwarded. - final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers = + private final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers = new SparseArray<CrossProfileIntentResolver>(); final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>(); @@ -467,7 +467,7 @@ public final class Settings { } private static void invalidatePackageCache() { - PackageManager.invalidatePackageInfoCache(); + PackageManagerService.invalidatePackageInfoCache(); ChangeIdStateCache.invalidate(); } @@ -1312,8 +1312,7 @@ public final class Settings { PreferredActivity pa = new PreferredActivity(parser); if (pa.mPref.getParseError() == null) { final PreferredIntentResolver resolver = editPreferredActivitiesLPw(userId); - ArrayList<PreferredActivity> pal = resolver.findFilters(pa); - if (pal == null || pal.size() == 0 || pa.mPref.mAlways) { + if (resolver.shouldAddPreferredActivity(pa)) { resolver.addFilter(pa); } } else { @@ -5543,4 +5542,130 @@ public final class Settings { } } } + + /** + * Accessor for preferred activities + */ + PersistentPreferredIntentResolver getPersistentPreferredActivities(int userId) { + return mPersistentPreferredActivities.get(userId); + } + + PreferredIntentResolver getPreferredActivities(int userId) { + return mPreferredActivities.get(userId); + } + + CrossProfileIntentResolver getCrossProfileIntentResolvers(int userId) { + return mCrossProfileIntentResolvers.get(userId); + } + + /** This method takes a specific user id as well as UserHandle.USER_ALL. */ + void clearPackagePreferredActivities(String packageName, + @NonNull SparseBooleanArray outUserChanged, int userId) { + ArrayList<PreferredActivity> removed = null; + for (int i = 0; i < mPreferredActivities.size(); i++) { + final int thisUserId = mPreferredActivities.keyAt(i); + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + if (userId != UserHandle.USER_ALL && userId != thisUserId) { + continue; + } + Iterator<PreferredActivity> it = pir.filterIterator(); + while (it.hasNext()) { + PreferredActivity pa = it.next(); + // Mark entry for removal only if it matches the package name + // and the entry is of type "always". + if (packageName == null + || (pa.mPref.mComponent.getPackageName().equals(packageName) + && pa.mPref.mAlways)) { + if (removed == null) { + removed = new ArrayList<>(); + } + removed.add(pa); + } + } + if (removed != null) { + for (int j = 0; j < removed.size(); j++) { + PreferredActivity pa = removed.get(j); + pir.removeFilter(pa); + } + outUserChanged.put(thisUserId, true); + } + } + } + + boolean clearPackagePersistentPreferredActivities(String packageName, int userId) { + ArrayList<PersistentPreferredActivity> removed = null; + boolean changed = false; + for (int i = 0; i < mPersistentPreferredActivities.size(); i++) { + final int thisUserId = mPersistentPreferredActivities.keyAt(i); + PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.valueAt(i); + if (userId != thisUserId) { + continue; + } + Iterator<PersistentPreferredActivity> it = ppir.filterIterator(); + while (it.hasNext()) { + PersistentPreferredActivity ppa = it.next(); + // Mark entry for removal only if it matches the package name. + if (ppa.mComponent.getPackageName().equals(packageName)) { + if (removed == null) { + removed = new ArrayList<>(); + } + removed.add(ppa); + } + } + if (removed != null) { + for (int j = 0; j < removed.size(); j++) { + PersistentPreferredActivity ppa = removed.get(j); + ppir.removeFilter(ppa); + } + changed = true; + } + } + return changed; + } + + ArrayList<Integer> systemReady(ComponentResolver resolver) { + // Verify that all of the preferred activity components actually + // exist. It is possible for applications to be updated and at + // that point remove a previously declared activity component that + // had been set as a preferred activity. We try to clean this up + // the next time we encounter that preferred activity, but it is + // possible for the user flow to never be able to return to that + // situation so here we do a validity check to make sure we haven't + // left any junk around. + ArrayList<Integer> changed = new ArrayList<>(); + ArrayList<PreferredActivity> removed = new ArrayList<>(); + for (int i = 0; i < mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + removed.clear(); + for (PreferredActivity pa : pir.filterSet()) { + if (!resolver.isActivityDefined(pa.mPref.mComponent)) { + removed.add(pa); + } + } + if (removed.size() > 0) { + for (int r = 0; r < removed.size(); r++) { + PreferredActivity pa = removed.get(r); + Slog.w(TAG, "Removing dangling preferred activity: " + + pa.mPref.mComponent); + pir.removeFilter(pa); + } + changed.add(mPreferredActivities.keyAt(i)); + } + } + return changed; + } + + void dumpPreferred(PrintWriter pw, DumpState dumpState, String packageName) { + for (int i = 0; i < mPreferredActivities.size(); i++) { + PreferredIntentResolver pir = mPreferredActivities.valueAt(i); + int user = mPreferredActivities.keyAt(i); + if (pir.dump(pw, + dumpState.getTitlePrinted() + ? "\nPreferred Activities User " + user + ":" + : "Preferred Activities User " + user + ":", " ", + packageName, true, false)) { + dumpState.setTitlePrinted(true); + } + } + } } diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java index fdfee773ea74..660874e7b3c3 100644 --- a/services/core/java/com/android/server/pm/ShareTargetInfo.java +++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java @@ -17,10 +17,11 @@ package com.android.server.pm; import android.annotation.NonNull; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; @@ -123,7 +124,7 @@ class ShareTargetInfo { return strBuilder.toString(); } - void saveToXml(@NonNull XmlSerializer out) throws IOException { + void saveToXml(@NonNull TypedXmlSerializer out) throws IOException { out.startTag(null, TAG_SHARE_TARGET); ShortcutService.writeAttr(out, ATTR_TARGET_CLASS, mTargetClass); @@ -149,7 +150,7 @@ class ShareTargetInfo { out.endTag(null, TAG_SHARE_TARGET); } - static ShareTargetInfo loadFromXml(XmlPullParser parser) + static ShareTargetInfo loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final String targetClass = ShortcutService.parseStringAttribute(parser, ATTR_TARGET_CLASS); final ArrayList<ShareTargetInfo.TargetData> targetData = new ArrayList<>(); @@ -178,7 +179,7 @@ class ShareTargetInfo { targetClass, categories.toArray(new String[categories.size()])); } - private static ShareTargetInfo.TargetData parseTargetData(XmlPullParser parser) { + private static ShareTargetInfo.TargetData parseTargetData(TypedXmlPullParser parser) { final String scheme = ShortcutService.parseStringAttribute(parser, ATTR_SCHEME); final String host = ShortcutService.parseStringAttribute(parser, ATTR_HOST); final String port = ShortcutService.parseStringAttribute(parser, ATTR_PORT); diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java index 0ebe596111f2..2960bc9a3790 100644 --- a/services/core/java/com/android/server/pm/ShortcutLauncher.java +++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java @@ -24,6 +24,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -36,15 +38,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -225,7 +224,7 @@ class ShortcutLauncher extends ShortcutPackageItem { * Persist. */ @Override - public void saveToXml(XmlSerializer out, boolean forBackup) + public void saveToXml(TypedXmlSerializer out, boolean forBackup) throws IOException { if (forBackup && !getPackageInfo().isBackupAllowed()) { // If an launcher app doesn't support backup&restore, then nothing to do. @@ -278,11 +277,8 @@ class ShortcutLauncher extends ShortcutPackageItem { } try { - final BufferedInputStream bis = new BufferedInputStream(in); - ShortcutLauncher ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -313,7 +309,7 @@ class ShortcutLauncher extends ShortcutPackageItem { /** * Load. */ - public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser, + public static ShortcutLauncher loadFromXml(TypedXmlPullParser parser, ShortcutUser shortcutUser, int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException { final String launcherPackageName = ShortcutService.parseStringAttribute(parser, ATTR_PACKAGE_NAME); diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index f6c60ad6e2cf..0ac0c8d95423 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -35,6 +35,8 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -52,15 +54,12 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -1571,7 +1570,7 @@ class ShortcutPackage extends ShortcutPackageItem { } @Override - public void saveToXml(@NonNull XmlSerializer out, boolean forBackup) + public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { final int size = mShortcuts.size(); final int shareTargetSize = mShareTargets.size(); @@ -1601,7 +1600,7 @@ class ShortcutPackage extends ShortcutPackageItem { out.endTag(null, TAG_ROOT); } - private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup, + private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup, boolean appSupportsBackup) throws IOException, XmlPullParserException { @@ -1734,11 +1733,8 @@ class ShortcutPackage extends ShortcutPackageItem { } try { - final BufferedInputStream bis = new BufferedInputStream(in); - ShortcutPackage ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -1767,7 +1763,7 @@ class ShortcutPackage extends ShortcutPackageItem { } public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser, - XmlPullParser parser, boolean fromBackup) + TypedXmlPullParser parser, boolean fromBackup) throws IOException, XmlPullParserException { final String packageName = ShortcutService.parseStringAttribute(parser, @@ -1814,7 +1810,7 @@ class ShortcutPackage extends ShortcutPackageItem { return ret; } - private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName, + private static ShortcutInfo parseShortcut(TypedXmlPullParser parser, String packageName, @UserIdInt int userId, boolean fromBackup) throws IOException, XmlPullParserException { String id; @@ -1948,7 +1944,7 @@ class ShortcutPackage extends ShortcutPackageItem { disabledReason, persons.toArray(new Person[persons.size()]), locusId); } - private static Intent parseIntent(XmlPullParser parser) + private static Intent parseIntent(TypedXmlPullParser parser) throws IOException, XmlPullParserException { Intent intent = ShortcutService.parseIntentAttribute(parser, @@ -1978,7 +1974,7 @@ class ShortcutPackage extends ShortcutPackageItem { return intent; } - private static Person parsePerson(XmlPullParser parser) + private static Person parsePerson(TypedXmlPullParser parser) throws IOException, XmlPullParserException { CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME); String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI); diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java index 8c7871ffaf96..fce66108bede 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java @@ -23,6 +23,8 @@ import android.content.pm.ShortcutInfo; import android.content.pm.Signature; import android.content.pm.SigningInfo; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; @@ -32,7 +34,6 @@ import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; @@ -205,7 +206,7 @@ class ShortcutPackageInfo { mSigHashes = BackupUtils.hashSignatureArray(signatures); } - public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup) + public void saveToXml(ShortcutService s, TypedXmlSerializer out, boolean forBackup) throws IOException { if (forBackup && !mBackupAllowedInitialized) { s.wtf("Backup happened before mBackupAllowed is initialized."); @@ -236,7 +237,7 @@ class ShortcutPackageInfo { out.endTag(null, TAG_ROOT); } - public void loadFromXml(XmlPullParser parser, boolean fromBackup) + public void loadFromXml(TypedXmlPullParser parser, boolean fromBackup) throws IOException, XmlPullParserException { // Don't use the version code from the backup file. final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION, diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index 801c6cbb8f46..829133c9854a 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -20,16 +20,15 @@ import android.content.pm.PackageInfo; import android.content.pm.ShortcutInfo; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlSerializer; +import android.util.Xml; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -146,7 +145,7 @@ abstract class ShortcutPackageItem { protected abstract void onRestored(int restoreBlockReason); - public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup) + public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException; public void saveToFile(File path, boolean forBackup) { @@ -154,18 +153,21 @@ abstract class ShortcutPackageItem { FileOutputStream os = null; try { os = file.startWrite(); - final BufferedOutputStream bos = new BufferedOutputStream(os); // Write to XML - XmlSerializer itemOut = new FastXmlSerializer(); - itemOut.setOutput(bos, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer itemOut; + if (forBackup) { + itemOut = Xml.newFastSerializer(); + itemOut.setOutput(os, StandardCharsets.UTF_8.name()); + } else { + itemOut = Xml.resolveSerializer(os); + } itemOut.startDocument(null, true); saveToXml(itemOut, forBackup); itemOut.endDocument(); - bos.flush(); os.flush(); file.finishWrite(os); } catch (XmlPullParserException | IOException e) { diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2d771820865d..c68fe81d0565 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -95,6 +95,8 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.TypedValue; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.IWindowManager; @@ -104,7 +106,6 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.internal.util.StatLogger; import com.android.server.LocalServices; @@ -119,10 +120,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -799,31 +797,31 @@ public class ShortcutService extends IShortcutService.Stub { // === Persisting === @Nullable - static String parseStringAttribute(XmlPullParser parser, String attribute) { + static String parseStringAttribute(TypedXmlPullParser parser, String attribute) { return parser.getAttributeValue(null, attribute); } - static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) { + static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute) { return parseLongAttribute(parser, attribute) == 1; } - static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) { + static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute, boolean def) { return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1; } - static int parseIntAttribute(XmlPullParser parser, String attribute) { + static int parseIntAttribute(TypedXmlPullParser parser, String attribute) { return (int) parseLongAttribute(parser, attribute); } - static int parseIntAttribute(XmlPullParser parser, String attribute, int def) { + static int parseIntAttribute(TypedXmlPullParser parser, String attribute, int def) { return (int) parseLongAttribute(parser, attribute, def); } - static long parseLongAttribute(XmlPullParser parser, String attribute) { + static long parseLongAttribute(TypedXmlPullParser parser, String attribute) { return parseLongAttribute(parser, attribute, 0); } - static long parseLongAttribute(XmlPullParser parser, String attribute, long def) { + static long parseLongAttribute(TypedXmlPullParser parser, String attribute, long def) { final String value = parseStringAttribute(parser, attribute); if (TextUtils.isEmpty(value)) { return def; @@ -837,7 +835,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) { + static ComponentName parseComponentNameAttribute(TypedXmlPullParser parser, String attribute) { final String value = parseStringAttribute(parser, attribute); if (TextUtils.isEmpty(value)) { return null; @@ -846,7 +844,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) { + static Intent parseIntentAttributeNoDefault(TypedXmlPullParser parser, String attribute) { final String value = parseStringAttribute(parser, attribute); Intent parsed = null; if (!TextUtils.isEmpty(value)) { @@ -860,7 +858,7 @@ public class ShortcutService extends IShortcutService.Stub { } @Nullable - static Intent parseIntentAttribute(XmlPullParser parser, String attribute) { + static Intent parseIntentAttribute(TypedXmlPullParser parser, String attribute) { Intent parsed = parseIntentAttributeNoDefault(parser, attribute); if (parsed == null) { // Default intent. @@ -869,7 +867,7 @@ public class ShortcutService extends IShortcutService.Stub { return parsed; } - static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, String value) throws IOException { if (TextUtils.isEmpty(value)) return; out.startTag(null, tag); @@ -877,16 +875,17 @@ public class ShortcutService extends IShortcutService.Stub { out.endTag(null, tag); } - static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, long value) throws IOException { writeTagValue(out, tag, Long.toString(value)); } - static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException { + static void writeTagValue(TypedXmlSerializer out, String tag, ComponentName name) + throws IOException { if (name == null) return; writeTagValue(out, tag, name.flattenToString()); } - static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle) + static void writeTagExtra(TypedXmlSerializer out, String tag, PersistableBundle bundle) throws IOException, XmlPullParserException { if (bundle == null) return; @@ -895,17 +894,18 @@ public class ShortcutService extends IShortcutService.Stub { out.endTag(null, tag); } - static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, CharSequence value) + throws IOException { if (TextUtils.isEmpty(value)) return; out.attribute(null, name, value.toString()); } - static void writeAttr(XmlSerializer out, String name, long value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, long value) throws IOException { writeAttr(out, name, String.valueOf(value)); } - static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, boolean value) throws IOException { if (value) { writeAttr(out, name, "1"); } else { @@ -913,12 +913,13 @@ public class ShortcutService extends IShortcutService.Stub { } } - static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, ComponentName comp) + throws IOException { if (comp == null) return; writeAttr(out, name, comp.flattenToString()); } - static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException { + static void writeAttr(TypedXmlSerializer out, String name, Intent intent) throws IOException { if (intent == null) return; writeAttr(out, name, intent.toUri(/* flags =*/ 0)); @@ -937,8 +938,7 @@ public class ShortcutService extends IShortcutService.Stub { outs = file.startWrite(); // Write to XML - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(outs, StandardCharsets.UTF_8.name()); + TypedXmlSerializer out = Xml.resolveSerializer(outs); out.startDocument(null, true); out.startTag(null, TAG_ROOT); @@ -966,8 +966,7 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, "Loading from " + file.getBaseFile()); } try (FileInputStream in = file.openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser = Xml.resolvePullParser(in); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -1043,18 +1042,20 @@ public class ShortcutService extends IShortcutService.Stub { private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os, boolean forBackup) throws IOException, XmlPullParserException { - final BufferedOutputStream bos = new BufferedOutputStream(os); - // Write to XML - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(bos, StandardCharsets.UTF_8.name()); + final TypedXmlSerializer out; + if (forBackup) { + out = Xml.newFastSerializer(); + out.setOutput(os, StandardCharsets.UTF_8.name()); + } else { + out = Xml.resolveSerializer(os); + } out.startDocument(null, true); getUserShortcutsLocked(userId).saveToXml(out, forBackup); out.endDocument(); - bos.flush(); os.flush(); } @@ -1098,11 +1099,14 @@ public class ShortcutService extends IShortcutService.Stub { boolean fromBackup) throws XmlPullParserException, IOException, InvalidFileFormatException { - final BufferedInputStream bis = new BufferedInputStream(is); - ShortcutUser ret = null; - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bis, StandardCharsets.UTF_8.name()); + TypedXmlPullParser parser; + if (fromBackup) { + parser = Xml.newFastPullParser(); + parser.setInput(is, StandardCharsets.UTF_8.name()); + } else { + parser = Xml.resolvePullParser(is); + } int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java index 5c1d8fb142e9..3e3aa677912b 100644 --- a/services/core/java/com/android/server/pm/ShortcutUser.java +++ b/services/core/java/com/android/server/pm/ShortcutUser.java @@ -26,6 +26,8 @@ import android.text.format.Formatter; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; @@ -38,7 +40,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.IOException; @@ -329,7 +330,7 @@ class ShortcutUser { }); } - public void saveToXml(XmlSerializer out, boolean forBackup) + public void saveToXml(TypedXmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { out.startTag(null, TAG_ROOT); @@ -371,7 +372,7 @@ class ShortcutUser { out.endTag(null, TAG_ROOT); } - private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi, + private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { if (forBackup) { if (spi.getPackageUserId() != spi.getOwnerUserId()) { @@ -408,7 +409,7 @@ class ShortcutUser { return new File(path, fileName); } - public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, + public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId, boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException { final ShortcutUser ret = new ShortcutUser(s, userId); boolean readShortcutItems = false; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index c51e75c716cc..cc814bcc7760 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -119,7 +119,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; @@ -175,6 +174,7 @@ public class UserManagerService extends IUserManager.Stub { private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated"; private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove"; private static final String ATTR_USER_VERSION = "version"; + private static final String ATTR_USER_TYPE_VERSION = "userTypeConfigVersion"; private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId"; private static final String ATTR_PROFILE_BADGE = "profileBadge"; private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId"; @@ -422,6 +422,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; + private int mUserTypeVersion = 0; private IAppOpsService mAppOpsService; @@ -2565,6 +2566,8 @@ public class UserManagerService extends IUserManager.Stub { parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); mUserVersion = parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion); + mUserTypeVersion = + parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); } // Pre-O global user restriction were stored as a single bundle (as opposed to per-user @@ -2627,7 +2630,7 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) { - upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion); + upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion); } /** @@ -2636,9 +2639,11 @@ public class UserManagerService extends IUserManager.Stub { */ @GuardedBy({"mRestrictionsLock", "mPackagesLock"}) @VisibleForTesting - void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) { + void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion, + int userTypeVersion) { Set<Integer> userIdsToWrite = new ArraySet<>(); final int originalVersion = mUserVersion; + final int originalUserTypeVersion = mUserTypeVersion; if (userVersion < 1) { // Assign a proper name for the owner, if not initialized correctly before UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM); @@ -2771,13 +2776,24 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 9; } + // Done with userVersion changes, moving on to deal with userTypeVersion upgrades + // Upgrade from previous user type to a new user type + final int newUserTypeVersion = UserTypeFactory.getUserTypeVersion(); + if (newUserTypeVersion > userTypeVersion) { + synchronized (mUsersLock) { + upgradeUserTypesLU(UserTypeFactory.getUserTypeUpgrades(), mUserTypes, + userTypeVersion, userIdsToWrite); + } + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); } else { mUserVersion = userVersion; + mUserTypeVersion = newUserTypeVersion; - if (originalVersion < mUserVersion) { + if (originalVersion < mUserVersion || originalUserTypeVersion < mUserTypeVersion) { for (int userId : userIdsToWrite) { UserData userData = getUserDataNoChecks(userId); if (userData != null) { @@ -2801,6 +2817,7 @@ public class UserManagerService extends IUserManager.Stub { UserData userData = putUserInfo(system); mNextSerialNumber = MIN_USER_ID; mUserVersion = USER_VERSION; + mUserTypeVersion = UserTypeFactory.getUserTypeVersion(); Bundle restrictions = new Bundle(); try { @@ -2991,6 +3008,7 @@ public class UserManagerService extends IUserManager.Stub { serializer.startTag(null, TAG_USERS); serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber); serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion); + serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); synchronized (mGuestRestrictions) { @@ -4957,6 +4975,7 @@ public class UserManagerService extends IUserManager.Stub { // Dump UserTypes pw.println(); + pw.println("User types version: " + mUserTypeVersion); pw.println("User types (" + mUserTypes.size() + " types):"); for (int i = 0; i < mUserTypes.size(); i++) { pw.println(" " + mUserTypes.keyAt(i) + ": "); @@ -5447,6 +5466,9 @@ public class UserManagerService extends IUserManager.Stub { * Returns the maximum number of users allowed for the given userTypeDetails per parent user. * This is applicable for user types that are {@link UserTypeDetails#isProfile()}. * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned. + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} */ private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) { final int defaultMax = userTypeDetails.getMaxAllowedPerParent(); @@ -5534,4 +5556,98 @@ public class UserManagerService extends IUserManager.Stub { } return mDevicePolicyManagerInternal; } + + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps, + @NonNull ArrayMap<String, UserTypeDetails> userTypes, + final int formerUserTypeVersion, + @NonNull Set<Integer> userIdsToWrite) { + for (UserTypeFactory.UserTypeUpgrade userTypeUpgrade : upgradeOps) { + if (DBG) { + Slog.i(LOG_TAG, "Upgrade: " + userTypeUpgrade.getFromType() + " to: " + + userTypeUpgrade.getToType() + " maxVersion: " + + userTypeUpgrade.getUpToVersion()); + } + + // upgrade user type if version up to getUpToVersion() + if (formerUserTypeVersion <= userTypeUpgrade.getUpToVersion()) { + for (int i = 0; i < mUsers.size(); i++) { + UserData userData = mUsers.valueAt(i); + if (userTypeUpgrade.getFromType().equals(userData.info.userType)) { + final UserTypeDetails newUserType = userTypes.get( + userTypeUpgrade.getToType()); + + if (newUserType == null) { + throw new IllegalStateException( + "Upgrade destination user type not defined: " + + userTypeUpgrade.getToType()); + } + + upgradeProfileToTypeLU(userData.info, newUserType); + userIdsToWrite.add(userData.info.id); + } + } + } + } + } + + /** + * Changes the user type of a profile to a new user type. + * @param userInfo The user to be updated. + * @param newUserType The new user type. + */ + @GuardedBy("mUsersLock") + @VisibleForTesting + void upgradeProfileToTypeLU(@NonNull UserInfo userInfo, @NonNull UserTypeDetails newUserType) { + Slog.i(LOG_TAG, "Upgrading user " + userInfo.id + + " from " + userInfo.userType + + " to " + newUserType.getName()); + + if (!userInfo.isProfile()) { + throw new IllegalStateException( + "Can only upgrade profile types. " + userInfo.userType + + " is not a profile type."); + } + + // Exceeded maximum profiles for parent user: log error, but allow upgrade + if (!canAddMoreProfilesToUser(newUserType.getName(), userInfo.profileGroupId, false)) { + Slog.w(LOG_TAG, + "Exceeded maximum profiles of type " + newUserType.getName() + " for user " + + userInfo.id + ". Maximum allowed= " + + newUserType.getMaxAllowedPerParent()); + } + + final UserTypeDetails oldUserType = mUserTypes.get(userInfo.userType); + final int oldFlags; + if (oldUserType != null) { + oldFlags = oldUserType.getDefaultUserInfoFlags(); + } else { + // if oldUserType is missing from config_user_types.xml -> can only assume FLAG_PROFILE + oldFlags = UserInfo.FLAG_PROFILE; + } + + //convert userData to newUserType + userInfo.userType = newUserType.getName(); + // remove old default flags and add newUserType's default flags + userInfo.flags = newUserType.getDefaultUserInfoFlags() | (userInfo.flags ^ oldFlags); + + // merge existing base restrictions with the new type's default restrictions + synchronized (mRestrictionsLock) { + if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) { + final Bundle newRestrictions = UserRestrictionsUtils.clone( + mBaseUserRestrictions.getRestrictions(userInfo.id)); + UserRestrictionsUtils.merge(newRestrictions, + newUserType.getDefaultRestrictions()); + updateUserRestrictionsInternalLR(newRestrictions, userInfo.id); + if (DBG) { + Slog.i(LOG_TAG, "Updated user " + userInfo.id + + " restrictions to " + newRestrictions); + } + } + } + + // re-compute badge index + userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType); + } } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index d0c3a95eafc7..0ac3030ba5dc 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -34,7 +34,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.provider.Settings.Global; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.util.Log; @@ -670,15 +669,6 @@ public class UserRestrictionsUtils { Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 0, userId); } break; - case UserManager.DISALLOW_CONFIG_LOCATION: - // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global - // kill switch. - if (newValue) { - android.provider.Settings.Global.putString( - context.getContentResolver(), - Global.LOCATION_GLOBAL_KILL_SWITCH, "0"); - } - break; case UserManager.DISALLOW_APPS_CONTROL: // Intentional fall-through case UserManager.DISALLOW_UNINSTALL_APPS: @@ -774,14 +764,6 @@ public class UserRestrictionsUtils { restriction = UserManager.DISALLOW_AMBIENT_DISPLAY; break; - case android.provider.Settings.Global.LOCATION_GLOBAL_KILL_SWITCH: - if ("0".equals(value)) { - return false; - } - restriction = UserManager.DISALLOW_CONFIG_LOCATION; - checkAllUser = true; - break; - case android.provider.Settings.System.SCREEN_BRIGHTNESS: case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT: case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE: diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java index d840e5d8b882..5fa46b9e4635 100644 --- a/services/core/java/com/android/server/pm/UserTypeDetails.java +++ b/services/core/java/com/android/server/pm/UserTypeDetails.java @@ -174,6 +174,9 @@ public final class UserTypeDetails { /** * Returns the maximum number of this user type allowed per parent (for user types, like * profiles, that have parents). + * Under certain circumstances (such as after a change-user-type) the max value can actually + * be exceeded: this is allowed in order to keep the device in a usable state. + * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU} * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit. */ public int getMaxAllowedPerParent() { diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java index ba8a2ba6a14e..1d3aecdca8dd 100644 --- a/services/core/java/com/android/server/pm/UserTypeFactory.java +++ b/services/core/java/com/android/server/pm/UserTypeFactory.java @@ -49,6 +49,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; /** @@ -73,14 +74,7 @@ public final class UserTypeFactory { * @return mapping from the name of each user type to its {@link UserTypeDetails} object */ public static ArrayMap<String, UserTypeDetails> getUserTypes() { - final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); - builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); - builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); - builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); - builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); - builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); - builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); - builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders(); try (XmlResourceParser parser = Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { @@ -94,6 +88,20 @@ public final class UserTypeFactory { return types; } + private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + + builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged()); + builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem()); + builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary()); + builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest()); + builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo()); + builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted()); + builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless()); + + return builders; + } + /** * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED} * configuration. @@ -232,6 +240,10 @@ public final class UserTypeFactory { isProfile = true; } else if ("full-type".equals(elementName)) { isProfile = false; + } else if ("change-user-type".equals(elementName)) { + // parsed in parseUserUpgrades + XmlUtils.skipCurrentTag(parser); + continue; } else { Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in " + parser.getPositionDescription()); @@ -291,7 +303,8 @@ public final class UserTypeFactory { while (XmlUtils.nextElementWithin(parser, depth)) { final String childName = parser.getName(); if ("default-restrictions".equals(childName)) { - final Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser); + final Bundle restrictions = UserRestrictionsUtils + .readRestrictions(XmlUtils.makeTyped(parser)); builder.setDefaultRestrictions(restrictions); } else if (isProfile && "badge-labels".equals(childName)) { setResAttributeArray(parser, builder::setBadgeLabels); @@ -387,4 +400,132 @@ public final class UserTypeFactory { } fcn.accept(result); } + + /** + * Returns the user type version of the config XML file. + * @return user type version defined in XML file, 0 if none. + */ + public static int getUserTypeVersion() { + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + return getUserTypeVersion(parser); + } + } + + @VisibleForTesting + static int getUserTypeVersion(XmlResourceParser parser) { + int version = 0; + + try { + XmlUtils.beginDocument(parser, "user-types"); + String versionValue = parser.getAttributeValue(null, "version"); + if (versionValue != null) { + try { + version = Integer.parseInt(versionValue); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in " + + parser.getPositionDescription(), e); + throw e; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return version; + } + + /** + * Obtains the user type upgrades for this device. + * @return The list of user type upgrades. + */ + public static List<UserTypeUpgrade> getUserTypeUpgrades() { + final List<UserTypeUpgrade> userUpgrades; + try (XmlResourceParser parser = + Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) { + userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser); + } + return userUpgrades; + } + + @VisibleForTesting + static List<UserTypeUpgrade> parseUserUpgrades( + ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) { + final List<UserTypeUpgrade> userUpgrades = new ArrayList<>(); + + try { + XmlUtils.beginDocument(parser, "user-types"); + for (XmlUtils.nextElement(parser); + parser.getEventType() != XmlResourceParser.END_DOCUMENT; + XmlUtils.nextElement(parser)) { + final String elementName = parser.getName(); + if ("change-user-type".equals(elementName)) { + final String fromType = parser.getAttributeValue(null, "from"); + final String toType = parser.getAttributeValue(null, "to"); + // Check that the base type doesn't change. + // Currently, only the base type of PROFILE is supported. + validateUserTypeIsProfile(fromType, builders); + validateUserTypeIsProfile(toType, builders); + + final int maxVersionToConvert; + try { + maxVersionToConvert = Integer.parseInt( + parser.getAttributeValue(null, "whenVersionLeq")); + } catch (NumberFormatException e) { + Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in " + + parser.getPositionDescription(), e); + throw e; + } + + UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType, + maxVersionToConvert); + userUpgrades.add(userTypeUpgrade); + continue; + } else { + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } catch (XmlPullParserException | IOException e) { + Slog.w(LOG_TAG, "Cannot read user type configuration file.", e); + } + + return userUpgrades; + } + + private static void validateUserTypeIsProfile(String userType, + ArrayMap<String, UserTypeDetails.Builder> builders) { + UserTypeDetails.Builder builder = builders.get(userType); + if (builder != null && builder.getBaseType() != FLAG_PROFILE) { + throw new IllegalArgumentException("Illegal upgrade of user type " + userType + + " : Can only upgrade profiles user types"); + } + } + + /** + * Contains details required for an upgrade operation for {@link UserTypeDetails}; + */ + public static class UserTypeUpgrade { + private final String mFromType; + private final String mToType; + private final int mUpToVersion; + + public UserTypeUpgrade(String fromType, String toType, int upToVersion) { + mFromType = fromType; + mToType = toType; + mUpToVersion = upToVersion; + } + + public String getFromType() { + return mFromType; + } + + public String getToType() { + return mToType; + } + + public int getUpToVersion() { + return mUpToVersion; + } + } } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index e5c93a32ae90..165647205a98 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -63,6 +63,7 @@ import android.util.SparseArray; import android.util.TypedXmlPullParser; import android.util.Xml; +import com.android.internal.R; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; @@ -72,7 +73,6 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.SyncAda import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -766,9 +766,14 @@ public final class DefaultPermissionGrantPolicy { grantSystemFixedPermissionsToSystemPackage(pm, wearPackage, userId, PHONE_PERMISSIONS); // Fitness tracking on watches - grantPermissionsToSystemPackage(pm, + if (mContext.getResources().getBoolean(R.bool.config_trackerAppNeedsPermissions)) { + Log.d(TAG, "Wear: Skipping permission grant for Default fitness tracker app : " + + wearPackage); + } else { + grantPermissionsToSystemPackage(pm, getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId, SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS); + } } // Print Spooler @@ -1408,11 +1413,8 @@ public final class DefaultPermissionGrantPolicy { Slog.w(TAG, "Default permissions file " + file + " cannot be read"); continue; } - try ( - InputStream str = new BufferedInputStream(new FileInputStream(file)) - ) { - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(str, null); + try (InputStream str = new FileInputStream(file)) { + TypedXmlPullParser parser = Xml.resolvePullParser(str); parse(pm, parser, grantExceptions); } catch (XmlPullParserException | IOException e) { Slog.w(TAG, "Error reading default permissions file " + file, e); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index fb1ed2f6b58b..52bb3d772387 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -205,6 +205,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; + /** All storage permissions */ + private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>(); + /** If the permission of the value is granted, so is the key */ private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>(); @@ -213,6 +216,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { Manifest.permission.ACCESS_FINE_LOCATION); FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS, Manifest.permission.INTERACT_ACROSS_USERS_FULL); + STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION); } /** Lock to protect internal data access */ @@ -1243,47 +1249,53 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " - + userId); - return null; - } + return getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } - int queryFlags = 0; - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } + @Nullable + private List<String> getAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, + @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + synchronized (mLock) { + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); + return null; + } - ArrayList<String> whitelistedPermissions = null; + int queryFlags = 0; + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } + if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) { + queryFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + } - final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); - for (int i = 0; i < permissionCount; i++) { - final String permissionName = pkg.getRequestedPermissions().get(i); - final int currentFlags = - uidState.getPermissionFlags(permissionName); - if ((currentFlags & queryFlags) != 0) { - if (whitelistedPermissions == null) { - whitelistedPermissions = new ArrayList<>(); - } - whitelistedPermissions.add(permissionName); + ArrayList<String> allowlistedPermissions = null; + + final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); + for (int i = 0; i < permissionCount; i++) { + final String permissionName = pkg.getRequestedPermissions().get(i); + final int currentFlags = + uidState.getPermissionFlags(permissionName); + if ((currentFlags & queryFlags) != 0) { + if (allowlistedPermissions == null) { + allowlistedPermissions = new ArrayList<>(); } + allowlistedPermissions.add(permissionName); } - - return whitelistedPermissions; } - } finally { - Binder.restoreCallingIdentity(identity); + + return allowlistedPermissions; } } @@ -1429,8 +1441,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, - new int[] { userId }); + setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); } finally { Binder.restoreCallingIdentity(identity); } @@ -1453,13 +1464,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return setAutoRevokeExemptedInternal(pkg, whitelisted, userId); } - private void setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds) { - for (final int userId : userIds) { - setAutoRevokeExemptedInternal(pkg, exempted, userId); - } - } - private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, @UserIdInt int userId) { final int packageUid = UserHandle.getUid(userId, pkg.getUid()); @@ -2358,6 +2362,48 @@ public class PermissionManagerService extends IPermissionManager.Stub { } /** + * If the app is updated, and has scoped storage permissions, then it is possible that the + * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. + * @param newPackage The new package that was installed + * @param oldPackage The old package that was updated + */ + private void revokeStoragePermissionsIfScopeExpandedInternal( + @NonNull AndroidPackage newPackage, + @NonNull AndroidPackage oldPackage) { + boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q + && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q; + boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q + && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q; + boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage() + && newPackage.isRequestLegacyExternalStorage(); + + if (!newlyRequestsLegacy && !downgradedSdk) { + return; + } + + final int callingUid = Binder.getCallingUid(); + final int userId = UserHandle.getUserId(newPackage.getUid()); + int numRequestedPermissions = newPackage.getRequestedPermissions().size(); + for (int i = 0; i < numRequestedPermissions; i++) { + PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i), + newPackage.getPackageName(), 0); + if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) { + continue; + } + + EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(), + "Revoking permission " + permInfo.name + " from package " + + newPackage.getPackageName() + " as either the sdk downgraded " + + downgradedSdk + " or newly requested legacy full storage " + + newlyRequestsLegacy); + + revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(), + false, callingUid, userId, null, mDefaultPermissionCallback); + } + + } + + /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of * the new group auto-granted. @@ -3775,13 +3821,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private void grantRequestedRuntimePermissionsInternal(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds) { - for (int userId : userIds) { - grantRequestedRuntimePermissionsForUser(pkg, permissions, userId); - } - } - - private void grantRequestedRuntimePermissionsForUser(@NonNull AndroidPackage pkg, @Nullable List<String> permissions, int userId) { final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3828,123 +3867,119 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags, - @UserIdInt int[] userIds) { - SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>(); + @UserIdInt int userId) { + ArraySet<String> oldGrantedRestrictedPermissions = null; boolean updatePermissions = false; final int permissionCount = pkg.getRequestedPermissions().size(); final int myUid = Process.myUid(); - for (int i = 0; i < userIds.length; i++) { - int userId = userIds[i]; - - for (int j = 0; j < permissionCount; j++) { - final String permissionName = pkg.getRequestedPermissions().get(j); - - final boolean isGranted; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permissionName); - if (bp == null || !bp.isHardOrSoftRestricted()) { - continue; - } + for (int j = 0; j < permissionCount; j++) { + final String permissionName = pkg.getRequestedPermissions().get(j); - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); + final boolean isGranted; + synchronized (mLock) { + final Permission bp = mRegistry.getPermission(permissionName); + if (bp == null || !bp.isHardOrSoftRestricted()) { + continue; } - if (isGranted) { - if (oldGrantedRestrictedPermissions.get(userId) == null) { - oldGrantedRestrictedPermissions.put(userId, new ArraySet<>()); - } - oldGrantedRestrictedPermissions.get(userId).add(permissionName); + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + + " and user " + userId); + continue; } + isGranted = uidState.isPermissionGranted(permissionName); + } - final int oldFlags = getPermissionFlagsInternal(permissionName, - pkg.getPackageName(), myUid, userId); - - int newFlags = oldFlags; - int mask = 0; - int whitelistFlagsCopy = allowlistFlags; - while (whitelistFlagsCopy != 0) { - final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy); - whitelistFlagsCopy &= ~flag; - switch (flag) { - case FLAG_PERMISSION_WHITELIST_SYSTEM: { - mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } + if (isGranted) { + if (oldGrantedRestrictedPermissions == null) { + oldGrantedRestrictedPermissions = new ArraySet<>(); + } + oldGrantedRestrictedPermissions.add(permissionName); + } + + final int oldFlags = getPermissionFlagsInternal(permissionName, + pkg.getPackageName(), myUid, userId); + + int newFlags = oldFlags; + int mask = 0; + int whitelistFlagsCopy = allowlistFlags; + while (whitelistFlagsCopy != 0) { + final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy); + whitelistFlagsCopy &= ~flag; + switch (flag) { + case FLAG_PERMISSION_WHITELIST_SYSTEM: { + mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; } - break; - case FLAG_PERMISSION_WHITELIST_UPGRADE: { - mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } + } + break; + case FLAG_PERMISSION_WHITELIST_UPGRADE: { + mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; } - break; - case FLAG_PERMISSION_WHITELIST_INSTALLER: { - mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } + } + break; + case FLAG_PERMISSION_WHITELIST_INSTALLER: { + mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; } - break; - case FLAG_PERMISSION_ALLOWLIST_ROLE: { - mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; - } + } + break; + case FLAG_PERMISSION_ALLOWLIST_ROLE: { + mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + if (permissions != null && permissions.contains(permissionName)) { + newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; + } else { + newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT; } - break; } + break; } + } - if (oldFlags == newFlags) { - continue; - } + if (oldFlags == newFlags) { + continue; + } - updatePermissions = true; - - final boolean wasWhitelisted = (oldFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - final boolean isWhitelisted = (newFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - - // If the permission is policy fixed as granted but it is no longer - // on any of the whitelists we need to clear the policy fixed flag - // as whitelisting trumps policy i.e. policy cannot grant a non - // grantable permission. - if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - if (!isWhitelisted && isGranted) { - mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; - newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; - } - } + updatePermissions = true; + + final boolean wasWhitelisted = (oldFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + final boolean isWhitelisted = (newFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - // If we are whitelisting an app that does not support runtime permissions - // we need to make sure it goes through the permission review UI at launch. - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M - && !wasWhitelisted && isWhitelisted) { - mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + // If the permission is policy fixed as granted but it is no longer + // on any of the whitelists we need to clear the policy fixed flag + // as whitelisting trumps policy i.e. policy cannot grant a non + // grantable permission. + if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { + if (!isWhitelisted && isGranted) { + mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; + newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; } + } - updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags, - myUid, userId, false, null /*callback*/); + // If we are whitelisting an app that does not support runtime permissions + // we need to make sure it goes through the permission review UI at launch. + if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M + && !wasWhitelisted && isWhitelisted) { + mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; } + + updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags, + myUid, userId, false, null /*callback*/); } if (updatePermissions) { @@ -3952,31 +3987,27 @@ public class PermissionManagerService extends IPermissionManager.Stub { restorePermissionState(pkg, false, pkg.getPackageName(), mDefaultPermissionCallback); // If this resulted in losing a permission we need to kill the app. - for (int i = 0; i < userIds.length; i++) { - int userId = userIds[i]; - ArraySet<String> oldPermsForUser = oldGrantedRestrictedPermissions.get(userId); - if (oldPermsForUser == null) { - continue; - } + if (oldGrantedRestrictedPermissions == null) { + return; + } - final int oldGrantedCount = oldPermsForUser.size(); - for (int j = 0; j < oldGrantedCount; j++) { - final String permissionName = oldPermsForUser.valueAt(j); - // Sometimes we create a new permission state instance during update. - final boolean isGranted; - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); - } - if (!isGranted) { - mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null); - break; + final int oldGrantedCount = oldGrantedRestrictedPermissions.size(); + for (int j = 0; j < oldGrantedCount; j++) { + final String permissionName = oldGrantedRestrictedPermissions.valueAt(j); + // Sometimes we create a new permission state instance during update. + final boolean isGranted; + synchronized (mLock) { + final UidPermissionState uidState = getUidStateLocked(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + + " and user " + userId); + continue; } + isGranted = uidState.isPermissionGranted(permissionName); + } + if (!isGranted) { + mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null); + break; } } } @@ -4884,6 +4915,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { AsyncTask.execute(() -> { if (hasOldPkg) { revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); + revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); } if (hasPermissionDefinitionChanges) { revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( @@ -4914,6 +4946,34 @@ public class PermissionManagerService extends IPermissionManager.Stub { return true; } + private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, + @UserIdInt int userId) { + addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions, + FLAG_PERMISSION_WHITELIST_INSTALLER, userId); + if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED + || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { + setAutoRevokeExemptedInternal(pkg, + autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); + } + grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId); + } + + private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, + @NonNull List<String> allowlistedRestrictedPermissions, + @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + List<String> permissions = getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); + if (permissions != null) { + ArraySet<String> permissionSet = new ArraySet<>(permissions); + permissionSet.addAll(allowlistedRestrictedPermissions); + permissions = new ArrayList<>(permissionSet); + } else { + permissions = allowlistedRestrictedPermissions; + } + setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); + } + private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) { removeAllPermissionsInternal(pkg); } @@ -5080,28 +5140,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); } @Override - public void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - grantRequestedRuntimePermissionsInternal(pkg, permissions, userIds); - } - @Override - public void setAllowlistedRestrictedPermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, allowlistFlags, userIds); - } - @Override - public void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAutoRevokeExemptedInternal(pkg, exempted, userIds); - } - @Override public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { PermissionManagerService.this .updatePermissions(packageName, pkg, mDefaultPermissionCallback); @@ -5372,6 +5410,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @UserIdInt int userId) { + Objects.requireNonNull(pkg, "pkg"); + Objects.requireNonNull(grantedPermissions, "grantedPermissions"); + Objects.requireNonNull(allowlistedRestrictedPermissions, + "allowlistedRestrictedPermissions"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions, + autoRevokePermissionsMode, userId); + } + + @Override public void onPackageRemoved(@NonNull AndroidPackage pkg) { Objects.requireNonNull(pkg); onPackageRemovedInternal(pkg); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 1becbedc29fb..457fe36ca2b8 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -20,7 +20,6 @@ import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; @@ -190,42 +189,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @UserIdInt int userId); /** - * Grant the requested runtime permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be granted, or {@code null} for - * granting all the requested permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds); - - /** - * Set the allowlisted restricted permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be allowlisted, or {@code null} - * for allowlisting all the requested restricted permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAllowlistedRestrictedPermissions( - @NonNull AndroidPackage pkg, @Nullable List<String> permissions, - @PackageManager.PermissionWhitelistFlags int allowlistFlags, @NonNull int[] userIds); - - /** - * Set whether a package is exempted from auto revoke. - * - * @param pkg the package - * @param exempted whether the package is exempted from auto revoke - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds); - - /** * Update permissions when a package changed. * * <p><ol> @@ -526,6 +489,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @Nullable AndroidPackage oldPkg); /** + * Callback when a package has been installed for certain users. + * + * @param pkg the installed package + * @param grantedPermissions the permissions to be granted + * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted + * @param autoRevokePermissionsMode the auto revoke permissions mode for this package + * @param userId the user ID this package is installed for + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @UserIdInt int userId); + + /** * Callback when a package has been removed. * * @param pkg the removed package diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java index dd287ca6ed00..1e4e0a6a04bc 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java @@ -224,10 +224,9 @@ public class BatterySaverController implements BatterySaverPolicyListener { mFileUpdater = new FileUpdater(context); mBatterySavingStats = batterySavingStats; + // TODO(79580230): remove plugin code and maybe screen on/off listeners? // Initialize plugins. - mPlugins = new Plugin[] { - new BatterySaverLocationPlugin(mContext) - }; + mPlugins = new Plugin[0]; PowerManager.invalidatePowerSaveModeCaches(); } diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java deleted file mode 100644 index a77d133f6c32..000000000000 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.power.batterysaver; - -import android.content.Context; -import android.os.PowerManager; -import android.provider.Settings; -import android.provider.Settings.Global; -import android.util.Slog; - -import com.android.server.power.batterysaver.BatterySaverController.Plugin; - -public class BatterySaverLocationPlugin implements Plugin { - private static final String TAG = "BatterySaverLocationPlugin"; - - private static final boolean DEBUG = BatterySaverController.DEBUG; - - private final Context mContext; - - public BatterySaverLocationPlugin(Context context) { - mContext = context; - } - - @Override - public void onBatterySaverChanged(BatterySaverController caller) { - if (DEBUG) { - Slog.d(TAG, "onBatterySaverChanged"); - } - updateLocationState(caller); - } - - @Override - public void onSystemReady(BatterySaverController caller) { - if (DEBUG) { - Slog.d(TAG, "onSystemReady"); - } - updateLocationState(caller); - } - - private void updateLocationState(BatterySaverController caller) { - final boolean kill = - (caller.getBatterySaverPolicy().getGpsMode() - == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) - && !caller.isInteractive(); - - if (DEBUG) { - Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location."); - } - Settings.Global.putInt(mContext.getContentResolver(), - Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0); - } -} diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java index 98293570507c..c9595c2eec2b 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java @@ -177,8 +177,28 @@ public class PowerStatsDataStorage { // filename, so any files that don't match the current version number can be deleted. File[] files = mDataStorageDir.listFiles(); for (int i = 0; i < files.length; i++) { - if (!files[i].getName().matches(dataStorageFilename + "(.*)")) { - files[i].delete(); + // Meter and model files are stored in the same directory. + // + // The format of filenames on disk is: + // log.powerstats.meter.version.timestamp + // log.powerstats.model.version.timestamp + // + // The format of dataStorageFilenames is: + // log.powerstats.meter.version + // log.powerstats.model.version + // + // A PowerStatsDataStorage object is created for meter and model data. Strip off + // the version and check that the current file we're checking starts with the stem + // (log.powerstats.meter or log.powerstats.model). If the stem matches and the + // version number is different, delete the old file. + int versionDot = dataStorageFilename.lastIndexOf('.'); + String beforeVersionDot = dataStorageFilename.substring(0, versionDot); + // Check that the stems match. + if (files[i].getName().startsWith(beforeVersionDot)) { + // Check that the version number matches. If not, delete the old file. + if (!files[i].getName().startsWith(dataStorageFilename)) { + files[i].delete(); + } } } diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index b33dc8fab9a5..7751397d9c31 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -28,6 +28,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.GuardedBy; @@ -391,8 +392,7 @@ public class RoleUserState { private void readLegacyFileLocked() { File file = getFile(mUserId); try (FileInputStream in = new AtomicFile(file).openRead()) { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parseXmlLocked(parser); Slog.i(LOG_TAG, "Read roles.xml successfully"); } catch (FileNotFoundException e) { @@ -402,7 +402,7 @@ public class RoleUserState { } } - private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException, + private void parseXmlLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { int type; int depth; @@ -421,9 +421,9 @@ public class RoleUserState { Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml"); } - private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException, + private void parseRolesLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { - mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); + mVersion = parser.getAttributeInt(null, ATTRIBUTE_VERSION); mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); mRoles.clear(); @@ -445,7 +445,7 @@ public class RoleUserState { } @NonNull - private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser) + private ArraySet<String> parseRoleHoldersLocked(@NonNull TypedXmlPullParser parser) throws IOException, XmlPullParserException { ArraySet<String> roleHolders = new ArraySet<>(); diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java index d2614e4698d1..4f3101dc318c 100644 --- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java +++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java @@ -51,13 +51,10 @@ import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.Preconditions; import com.android.server.pm.Installer; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -65,7 +62,6 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -319,12 +315,12 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { } @VisibleForTesting - static void saveToXml(XmlSerializer out, + static void saveToXml(TypedXmlSerializer out, List<CacheQuotaHint> requests, long bytesWhenCalculated) throws IOException { out.startDocument(null, true); out.startTag(null, CACHE_INFO_TAG); int requestSize = requests.size(); - out.attribute(null, ATTR_PREVIOUS_BYTES, Long.toString(bytesWhenCalculated)); + out.attributeLong(null, ATTR_PREVIOUS_BYTES, bytesWhenCalculated); for (int i = 0; i < requestSize; i++) { CacheQuotaHint request = requests.get(i); @@ -333,8 +329,8 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { if (uuid != null) { out.attribute(null, ATTR_UUID, request.getVolumeUuid()); } - out.attribute(null, ATTR_UID, Integer.toString(request.getUid())); - out.attribute(null, ATTR_QUOTA_IN_BYTES, Long.toString(request.getQuota())); + out.attributeInt(null, ATTR_UID, request.getUid()); + out.attributeLong(null, ATTR_QUOTA_IN_BYTES, request.getQuota()); out.endTag(null, TAG_QUOTA); } out.endTag(null, CACHE_INFO_TAG); @@ -364,8 +360,7 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { final List<CacheQuotaHint> quotas = new ArrayList<>(); long previousBytes; try { - previousBytes = Long.parseLong(parser.getAttributeValue( - null, ATTR_PREVIOUS_BYTES)); + previousBytes = parser.getAttributeLong(null, ATTR_PREVIOUS_BYTES); } catch (NumberFormatException e) { throw new IllegalStateException( "Previous bytes formatted incorrectly; aborting quota read."); @@ -389,14 +384,14 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { } @VisibleForTesting - static CacheQuotaHint getRequestFromXml(XmlPullParser parser) { + static CacheQuotaHint getRequestFromXml(TypedXmlPullParser parser) { try { String uuid = parser.getAttributeValue(null, ATTR_UUID); - int uid = Integer.parseInt(parser.getAttributeValue(null, ATTR_UID)); - long bytes = Long.parseLong(parser.getAttributeValue(null, ATTR_QUOTA_IN_BYTES)); + int uid = parser.getAttributeInt(null, ATTR_UID); + long bytes = parser.getAttributeLong(null, ATTR_QUOTA_IN_BYTES); return new CacheQuotaHint.Builder() .setVolumeUuid(uuid).setUid(uid).setQuota(bytes).build(); - } catch (NumberFormatException e) { + } catch (XmlPullParserException e) { Slog.e(TAG, "Invalid cache quota request, skipping."); return null; } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java index 5b6de0518999..4f3f9dce8adb 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyCallbackImpl.java @@ -16,6 +16,8 @@ package com.android.server.timedetector; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_NETWORK; +import static com.android.server.timedetector.TimeDetectorStrategy.ORIGIN_TELEPHONY; import static com.android.server.timedetector.TimeDetectorStrategy.stringToOrigin; import android.annotation.NonNull; @@ -30,6 +32,9 @@ import android.os.SystemProperties; import android.provider.Settings; import android.util.Slog; +import com.android.internal.R; +import com.android.server.timedetector.TimeDetectorStrategy.Origin; + import java.time.Instant; import java.util.Objects; @@ -50,6 +55,13 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat Long.max(Environment.getRootDirectory().lastModified(), Build.TIME)); /** + * By default telephony and network only suggestions are accepted and telephony takes + * precedence over network. + */ + private static final @Origin int[] DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES = + { ORIGIN_TELEPHONY, ORIGIN_NETWORK }; + + /** * If a newly calculated system clock time and the current system clock time differs by this or * more the system clock will actually be updated. Used to prevent the system clock being set * for only minor differences. @@ -76,14 +88,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat SystemProperties.getInt("ro.sys.time_detector_update_diff", SYSTEM_CLOCK_UPDATE_THRESHOLD_MILLIS_DEFAULT); - // TODO(b/172230856): Obtain these values from configuration. - String[] originStrings = { "telephony", "network" }; - int[] origins = new int[originStrings.length]; - for (int i = 0; i < originStrings.length; i++) { - int origin = stringToOrigin(originStrings[i]); - origins[i] = origin; - } - mOriginPriorities = origins; + mOriginPriorities = getOriginPriorities(context); } @Override @@ -106,7 +111,7 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat } @Override - public int[] getAutoOriginPriorities() { + public int[] autoOriginPriorities() { return mOriginPriorities; } @@ -145,4 +150,20 @@ public final class TimeDetectorStrategyCallbackImpl implements TimeDetectorStrat Slog.wtf(TAG, "WakeLock " + mWakeLock + " not held"); } } + + private static int[] getOriginPriorities(@NonNull Context context) { + String[] originStrings = + context.getResources().getStringArray(R.array.config_autoTimeSourcesPriority); + if (originStrings.length == 0) { + return DEFAULT_AUTOMATIC_TIME_ORIGIN_PRIORITIES; + } else { + int[] origins = new int[originStrings.length]; + for (int i = 0; i < originStrings.length; i++) { + int origin = stringToOrigin(originStrings[i]); + origins[i] = origin; + } + + return origins; + } + } } diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index d8cada55781c..b5d49cfbe9c8 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -18,6 +18,8 @@ package com.android.server.timedetector; import static com.android.server.timedetector.TimeDetectorStrategy.originToString; +import static java.util.stream.Collectors.joining; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; @@ -140,7 +142,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { * Returns the order to look at time suggestions when automatically detecting time. * See {@code #ORIGIN_} constants */ - @Origin int[] getAutoOriginPriorities(); + @Origin int[] autoOriginPriorities(); /** Acquire a suitable wake lock. Must be followed by {@link #releaseWakeLock()} */ void acquireWakeLock(); @@ -252,6 +254,14 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis()); ipw.println("mCallback.systemClockUpdateThresholdMillis()=" + mCallback.systemClockUpdateThresholdMillis()); + ipw.printf("mCallback.autoTimeLowerBound()=%s(%s)\n", + mCallback.autoTimeLowerBound(), + mCallback.autoTimeLowerBound().toEpochMilli()); + String priorities = + Arrays.stream(mCallback.autoOriginPriorities()) + .mapToObj(TimeDetectorStrategy::originToString) + .collect(joining(",", "[", "]")); + ipw.println("mCallback.autoOriginPriorities()=" + priorities); ipw.println("Time change log:"); ipw.increaseIndent(); // level 2 @@ -353,7 +363,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } // Try the different origins one at a time. - int[] originPriorities = mCallback.getAutoOriginPriorities(); + int[] originPriorities = mCallback.autoOriginPriorities(); for (int origin : originPriorities) { TimestampedValue<Long> newUtcTime = null; String cause = null; diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java index 7652c438c295..fd0df8d090ec 100644 --- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java +++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java @@ -129,7 +129,7 @@ final class PackageStatusStorage { @GuardedBy("this") private PackageStatus getPackageStatusLocked() throws ParseException { try (FileInputStream fis = mPackageStatusFile.openRead()) { - XmlPullParser parser = parseToPackageStatusTag(fis); + TypedXmlPullParser parser = parseToPackageStatusTag(fis); Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS); if (checkStatus == null) { return null; @@ -254,7 +254,7 @@ final class PackageStatusStorage { @GuardedBy("this") private int getCurrentOptimisticLockId() throws ParseException { try (FileInputStream fis = mPackageStatusFile.openRead()) { - XmlPullParser parser = parseToPackageStatusTag(fis); + TypedXmlPullParser parser = parseToPackageStatusTag(fis); return getIntAttribute(parser, ATTRIBUTE_OPTIMISTIC_LOCK_ID); } catch (IOException e) { ParseException e2 = new ParseException("Unable to read file", 0); @@ -264,7 +264,7 @@ final class PackageStatusStorage { } /** Returns a parser or throws ParseException, never returns null. */ - private static XmlPullParser parseToPackageStatusTag(FileInputStream fis) + private static TypedXmlPullParser parseToPackageStatusTag(FileInputStream fis) throws ParseException { try { TypedXmlPullParser parser = Xml.resolvePullParser(fis); @@ -358,7 +358,7 @@ final class PackageStatusStorage { } } - private static Integer getNullableIntAttribute(XmlPullParser parser, String attributeName) + private static Integer getNullableIntAttribute(TypedXmlPullParser parser, String attributeName) throws ParseException { String attributeValue = parser.getAttributeValue(null, attributeName); try { @@ -374,7 +374,7 @@ final class PackageStatusStorage { } } - private static int getIntAttribute(XmlPullParser parser, String attributeName) + private static int getIntAttribute(TypedXmlPullParser parser, String attributeName) throws ParseException { Integer value = getNullableIntAttribute(parser, attributeName); if (value == null) { diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java index 355c38544cb9..d3c9b3bbe7f5 100644 --- a/services/core/java/com/android/server/tv/PersistentDataStore.java +++ b/services/core/java/com/android/server/tv/PersistentDataStore.java @@ -26,6 +26,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.AtomicFile; import android.util.Slog; +import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -166,7 +167,7 @@ final class PersistentDataStore { return; } - XmlPullParser parser; + TypedXmlPullParser parser; try { parser = Xml.resolvePullParser(is); loadFromXml(parser); @@ -237,7 +238,7 @@ final class PersistentDataStore { private static final String ATTR_STRING = "string"; private static final String ATTR_ENABLED = "enabled"; - private void loadFromXml(XmlPullParser parser) + private void loadFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { XmlUtils.beginDocument(parser, TAG_TV_INPUT_MANAGER_STATE); final int outerDepth = parser.getDepth(); @@ -255,7 +256,7 @@ final class PersistentDataStore { } } - private void loadBlockedRatingsFromXml(XmlPullParser parser) + private void loadBlockedRatingsFromXml(TypedXmlPullParser parser) throws IOException, XmlPullParserException { final int outerDepth = parser.getDepth(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { @@ -270,7 +271,7 @@ final class PersistentDataStore { } } - private void saveToXml(XmlSerializer serializer) throws IOException { + private void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE); @@ -284,7 +285,7 @@ final class PersistentDataStore { } serializer.endTag(null, TAG_BLOCKED_RATINGS); serializer.startTag(null, TAG_PARENTAL_CONTROLS); - serializer.attribute(null, ATTR_ENABLED, Boolean.toString(mParentalControlsEnabled)); + serializer.attributeBoolean(null, ATTR_ENABLED, mParentalControlsEnabled); serializer.endTag(null, TAG_PARENTAL_CONTROLS); serializer.endTag(null, TAG_TV_INPUT_MANAGER_STATE); serializer.endDocument(); diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java index 367b966a46ed..beb11ed4ea0c 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java @@ -20,6 +20,7 @@ import android.media.tv.TvInputService.PriorityHintUseCaseType; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -114,9 +115,7 @@ public class UseCasePriorityHints { protected void parseInternal(InputStream in) throws IOException, XmlPullParserException { try { - XmlPullParser parser = Xml.newPullParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(in, null); + TypedXmlPullParser parser = Xml.resolvePullParser(in); parser.nextTag(); readUseCase(parser); in.close(); @@ -137,7 +136,7 @@ public class UseCasePriorityHints { } } - private void readUseCase(XmlPullParser parser) + private void readUseCase(TypedXmlPullParser parser) throws XmlPullParserException, IOException { parser.require(XmlPullParser.START_TAG, NS, "config"); while (parser.next() != XmlPullParser.END_TAG) { @@ -176,7 +175,7 @@ public class UseCasePriorityHints { } } - private void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + private void skip(TypedXmlPullParser parser) throws XmlPullParserException, IOException { if (parser.getEventType() != XmlPullParser.START_TAG) { throw new IllegalStateException(); } @@ -193,7 +192,7 @@ public class UseCasePriorityHints { } } - private int readAttributeToInt(String attributeName, XmlPullParser parser) { + private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) { return Integer.valueOf(parser.getAttributeValue(null, attributeName)); } @@ -203,7 +202,7 @@ public class UseCasePriorityHints { } @PriorityHintUseCaseType - private static int formatTypeToNum(String attributeName, XmlPullParser parser) { + private static int formatTypeToNum(String attributeName, TypedXmlPullParser parser) { String useCaseName = parser.getAttributeValue(null, attributeName); switch (useCaseName) { case "USE_CASE_BACKGROUND": diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index bbb5374ea68a..dcc15999d882 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -668,22 +668,22 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { if (TAG_URI_GRANT.equals(tag)) { final int sourceUserId; final int targetUserId; - final int userHandle = readIntAttribute(in, - ATTR_USER_HANDLE, UserHandle.USER_NULL); + final int userHandle = in.getAttributeInt(null, ATTR_USER_HANDLE, + UserHandle.USER_NULL); if (userHandle != UserHandle.USER_NULL) { // For backwards compatibility. sourceUserId = userHandle; targetUserId = userHandle; } else { - sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID); - targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID); + sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID); + targetUserId = in.getAttributeInt(null, ATTR_TARGET_USER_ID); } final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG); final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG); final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI)); - final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX); - final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS); - final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now); + final boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false); + final int modeFlags = in.getAttributeInt(null, ATTR_MODE_FLAGS); + final long createdTime = in.getAttributeLong(null, ATTR_CREATED_TIME, now); // Validity check that provider still belongs to source package // Both direct boot aware and unaware packages are fine as we @@ -1319,14 +1319,14 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { out.startTag(null, TAG_URI_GRANTS); for (UriPermission.Snapshot perm : persist) { out.startTag(null, TAG_URI_GRANT); - writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId); - writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId); + out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId); + out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId); out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg); out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg); out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri)); writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix); - writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags); - writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime); + out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags); + out.attributeLong(null, ATTR_CREATED_TIME, perm.persistedCreateTime); out.endTag(null, TAG_URI_GRANT); } out.endTag(null, TAG_URI_GRANTS); diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java index ab7e7f63cafd..ff609031b57c 100644 --- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java +++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.utils; +package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; @@ -54,6 +54,11 @@ public interface DeviceConfigInterface { boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue); /** + * @see DeviceConfig#getFloat + */ + float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue); + + /** * @see DeviceConfig#addOnPropertiesChangedListener */ void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor, @@ -96,6 +101,12 @@ public interface DeviceConfigInterface { } @Override + public float getFloat(@NonNull String namespace, @NonNull String name, + float defaultValue) { + return DeviceConfig.getFloat(namespace, name, defaultValue); + } + + @Override public void addOnPropertiesChangedListener(String namespace, Executor executor, DeviceConfig.OnPropertiesChangedListener listener) { DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener); diff --git a/services/core/java/com/android/server/utils/Snappable.java b/services/core/java/com/android/server/utils/Snappable.java new file mode 100644 index 000000000000..9b9460b8f757 --- /dev/null +++ b/services/core/java/com/android/server/utils/Snappable.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import android.annotation.NonNull; + +/** + * A class that implements Snappable can generate a read-only copy its instances. A + * snapshot is like a clone except that it is only required to support read-only class + * methods. Snapshots are immutable. Attempts to modify the state of a snapshot throw + * {@link UnsupporteOperationException}. + * @param <T> The type returned by the snapshot() method. + */ +public interface Snappable<T> { + + /** + * Create an immutable copy of the object, suitable for read-only methods. A snapshot + * is free to omit state that is only needed for mutating methods. + */ + @NonNull T snapshot(); +} diff --git a/services/core/java/com/android/server/utils/Snapshots.java b/services/core/java/com/android/server/utils/Snapshots.java new file mode 100644 index 000000000000..33b2bd48d802 --- /dev/null +++ b/services/core/java/com/android/server/utils/Snapshots.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import android.annotation.NonNull; +import android.util.SparseArray; +import android.util.SparseIntArray; +import android.util.SparseSetArray; + +/** + * A collection of useful methods for manipulating Snapshot classes. This is similar to + * java.util.Objects or java.util.Arrays. + */ +public class Snapshots { + + /** + * Return the snapshot of an object, if the object extends {@link Snapper}, or the object + * itself. + * @param o The object to be copied + * @return A snapshot of the object, if the object extends {@link Snapper} + */ + public static <T> T maybeSnapshot(T o) { + if (o instanceof Snappable) { + return ((Snappable<T>) o).snapshot(); + } else { + return o; + } + } + + /** + * Copy a SparseArray in a manner suitable for a snapshot. The destination must be + * empty. This is not a snapshot because the elements are copied by reference even if + * they are {@link Snappable}. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public <E> void copy(@NonNull SparseArray<E> dst, @NonNull SparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("copy destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Copy a SparseSetArray in a manner suitable for a snapshot. The destination must be + * empty. This is not a snapshot because the elements are copied by reference even if + * they are {@link Snappable}. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E> void copy(@NonNull SparseSetArray<E> dst, @NonNull SparseSetArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("copy destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final int size = src.sizeAt(i); + for (int j = 0; j < size; j++) { + dst.add(src.keyAt(i), src.valueAt(i, j)); + } + } + } + + /** + * Make <dst> a snapshot of <src> . + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public void snapshot(@NonNull SparseIntArray dst, @NonNull SparseIntArray src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i)); + } + } + + /** + * Make <dst> a "snapshot" of <src>. <dst> mst be empty. The destination is just a + * copy of the source except that if the source elements implement Snappable, then + * the elements in the destination will be snapshots of elements from the source. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E extends Snappable<E>> void snapshot(@NonNull SparseArray<E> dst, + @NonNull SparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + dst.put(src.keyAt(i), src.valueAt(i).snapshot()); + } + } + + /** + * Make <dst> a "snapshot" of <src>. <dst> mst be empty. The destination is a + * copy of the source except that snapshots are taken of the elements. + * @param dst The destination array. It must be empty. + * @param src The source array + */ + public static <E extends Snappable<E>> void snapshot(@NonNull SparseSetArray<E> dst, + @NonNull SparseSetArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final int size = src.sizeAt(i); + for (int j = 0; j < size; j++) { + dst.add(src.keyAt(i), src.valueAt(i, j).snapshot()); + } + } + } +} diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java index 94ab1d49807f..16400b186ab0 100644 --- a/services/core/java/com/android/server/utils/WatchableImpl.java +++ b/services/core/java/com/android/server/utils/WatchableImpl.java @@ -19,11 +19,15 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; import java.util.Objects; /** - * A concrete implementation of {@link Watchable} + * A concrete implementation of {@link Watchable}. This includes one commonly needed feature: + * the Watchable may be sealed, so that it throws an {@link IllegalStateException} if + * a change is detected. */ public class WatchableImpl implements Watchable { /** @@ -78,10 +82,37 @@ public class WatchableImpl implements Watchable { @Override public void dispatchChange(@Nullable Watchable what) { synchronized (mObservers) { + if (mSealed) { + throw new IllegalStateException("attempt to change a sealed object"); + } final int end = mObservers.size(); for (int i = 0; i < end; i++) { mObservers.get(i).onChange(what); } } } + + /** + * True if the object is sealed. + */ + @GuardedBy("mObservers") + private boolean mSealed = false; + + /** + * Freeze the {@link Watchable}. + **/ + public void seal() { + synchronized (mObservers) { + mSealed = true; + } + } + + /** + * Return the sealed state. + */ + public boolean isFrozen() { + synchronized (mObservers) { + return mSealed; + } + } } diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java index 7b3298086aba..e8065f140af7 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayMap.java +++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java @@ -18,9 +18,7 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; - import android.util.ArrayMap; -import android.util.Log; import java.util.Collection; import java.util.Collections; @@ -31,14 +29,17 @@ import java.util.Set; * WatchedArrayMap is an {@link android.util.ArrayMap} that can report changes to itself. If its * values are {@link Watchable} then the WatchedArrayMap will also report changes to the values. * A {@link Watchable} is notified only once, no matter how many times it is stored in the array. + * @param <K> The key type. + * @param <V> The value type. */ -public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> { +public class WatchedArrayMap<K, V> extends WatchableImpl + implements Map<K, V>, Snappable { // The storage private final ArrayMap<K, V> mStorage; // If true, the array is watching its children - private boolean mWatching = false; + private volatile boolean mWatching = false; // The local observer private final Watcher mObserver = new Watcher() { @@ -386,4 +387,38 @@ public class WatchedArrayMap<K, V> extends WatchableImpl implements Map<K, V> { onChanged(); return result; } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedArrayMap<K, V> snapshot() { + WatchedArrayMap<K, V> l = new WatchedArrayMap<>(); + snapshot(l, this); + return l; + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <K, V> void snapshot(@NonNull WatchedArrayMap<K, V> dst, + @NonNull WatchedArrayMap<K, V> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final V val = Snapshots.maybeSnapshot(src.valueAt(i)); + final K key = src.keyAt(i); + dst.put(key, val); + } + dst.seal(); + } } diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java index 4d43b682e113..6797c6eb7801 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java @@ -18,7 +18,6 @@ package com.android.server.utils; import android.annotation.NonNull; import android.annotation.Nullable; - import android.util.SparseArray; import java.util.ArrayList; @@ -28,14 +27,16 @@ import java.util.ArrayList; * array registers with the {@link Watchable}. The array registers only once with each * {@link Watchable} no matter how many times the {@link Watchable} is stored in the * array. + * @param <E> The element type, stored in the array. */ -public class WatchedSparseArray<E> extends WatchableImpl { +public class WatchedSparseArray<E> extends WatchableImpl + implements Snappable { // The storage private final SparseArray<E> mStorage; // If true, the array is watching its children - private boolean mWatching = false; + private volatile boolean mWatching = false; // The local observer private final Watcher mObserver = new Watcher() { @@ -398,4 +399,39 @@ public class WatchedSparseArray<E> extends WatchableImpl { public String toString() { return mStorage.toString(); } + + /** + * Create a copy of the array. If the element is a subclass of Snapper then the copy + * contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The returned snapshot is immutable. + * @return A new array whose elements are the elements of <this>. + */ + public WatchedSparseArray<E> snapshot() { + WatchedSparseArray<E> l = new WatchedSparseArray<>(); + snapshot(l, this); + return l; + } + + /** + * Make the destination a copy of the source. If the element is a subclass of Snapper then the + * copy contains snapshots of the elements. Otherwise the copy contains references to the + * elements. The destination must be initially empty. Upon return, the destination is + * immutable. + * @param dst The destination array. It must be empty. + * @param src The source array. It is not modified. + */ + public static <E> void snapshot(@NonNull WatchedSparseArray<E> dst, + @NonNull WatchedSparseArray<E> src) { + if (dst.size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = src.size(); + for (int i = 0; i < end; i++) { + final E val = Snapshots.maybeSnapshot(src.valueAt(i)); + final int key = src.keyAt(i); + dst.put(key, val); + } + dst.seal(); + } + } diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java index edf7d27b61dd..b845eea168a5 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java @@ -17,21 +17,20 @@ package com.android.server.utils; import android.annotation.NonNull; -import android.annotation.Nullable; - import android.util.SparseBooleanArray; /** * A watched variant of SparseBooleanArray. Changes to the array are notified to * registered {@link Watcher}s. */ -public class WatchedSparseBooleanArray extends WatchableImpl { +public class WatchedSparseBooleanArray extends WatchableImpl + implements Snappable { // The storage private final SparseBooleanArray mStorage; // A private convenience function - private void dispatchChange() { + private void onChanged() { dispatchChange(this); } @@ -81,7 +80,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void delete(int key) { mStorage.delete(key); - dispatchChange(); + onChanged(); } /** @@ -91,7 +90,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void removeAt(int index) { mStorage.removeAt(index); - dispatchChange(); + onChanged(); } /** @@ -102,7 +101,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void put(int key, boolean value) { if (mStorage.get(key) != value) { mStorage.put(key, value); - dispatchChange(); + onChanged(); } } @@ -164,7 +163,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void setValueAt(int index, boolean value) { if (mStorage.valueAt(index) != value) { mStorage.setValueAt(index, value); - dispatchChange(); + onChanged(); } } @@ -172,7 +171,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public void setKeyAt(int index, int key) { if (mStorage.keyAt(index) != key) { mStorage.setKeyAt(index, key); - dispatchChange(); + onChanged(); } } @@ -202,7 +201,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void clear() { mStorage.clear(); - dispatchChange(); + onChanged(); } /** @@ -211,7 +210,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl { */ public void append(int key, boolean value) { mStorage.append(key, value); - dispatchChange(); + onChanged(); } @Override @@ -233,4 +232,30 @@ public class WatchedSparseBooleanArray extends WatchableImpl { public String toString() { return mStorage.toString(); } + + /** + * Create a snapshot. The snapshot does not include any {@link Watchable} + * information. + */ + public WatchedSparseBooleanArray snapshot() { + WatchedSparseBooleanArray l = new WatchedSparseBooleanArray(this); + l.seal(); + return l; + } + + /** + * Make <this> a snapshot of the argument. Note that <this> is immutable when the + * method returns. <this> must be empty when the function is called. + * @param r The source array, which is copied into <this> + */ + public void snapshot(@NonNull WatchedSparseBooleanArray r) { + if (size() != 0) { + throw new IllegalArgumentException("snapshot destination is not empty"); + } + final int end = r.size(); + for (int i = 0; i < end; i++) { + put(r.keyAt(i), r.valueAt(i)); + } + seal(); + } } diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java index 12c6a7a397e3..fe51d7408153 100644 --- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java +++ b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java @@ -99,6 +99,7 @@ public abstract class LocalEventLog { private long mLastLogRealtimeMs; public LocalEventLog(int size) { + Preconditions.checkArgument(size > 0); mLog = new Log[size]; mLogSize = 0; mLogEndIndex = 0; @@ -163,7 +164,7 @@ public abstract class LocalEventLog { if (mLogSize == mLog.length) { // if log is full, size will remain the same, but update the start time - mStartRealtimeMs += event.getTimeDeltaMs(); + mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs(); } else { // otherwise add an item mLogSize++; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index e07540a0c746..c3d5874de609 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -39,7 +39,7 @@ import android.app.WallpaperColors; import android.app.WallpaperInfo; import android.app.WallpaperManager; import android.app.WallpaperManager.SetWallpaperFlags; -import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyManagerInternal; import android.app.backup.WallpaperBackupHelper; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -100,12 +100,12 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.server.EventLogTags; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.SystemService.TargetUser; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.WindowManagerInternal; @@ -114,7 +114,6 @@ import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedOutputStream; import java.io.File; @@ -125,7 +124,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -2848,10 +2846,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (!uidMatchPackage) { return false; // callingPackage was faked. } - - // TODO(b/144048540): DPM needs to take into account the userId, not just the package. - final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - if (dpm.isDeviceOwnerApp(callingPackage) || dpm.isProfileOwnerApp(callingPackage)) { + final DevicePolicyManagerInternal dpmi = + LocalServices.getService(DevicePolicyManagerInternal.class); + if (dpmi != null && dpmi.isDeviceOrProfileOwnerInCallingUser(callingPackage)) { return true; } final int callingUserId = UserHandle.getCallingUserId(); @@ -2909,11 +2906,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub private void saveSettingsLocked(int userId) { JournaledFile journal = makeJournaledFile(userId); FileOutputStream fstream = null; - BufferedOutputStream stream = null; try { fstream = new FileOutputStream(journal.chooseForWrite(), false); - stream = new BufferedOutputStream(fstream); - TypedXmlSerializer out = Xml.resolveSerializer(stream); + TypedXmlSerializer out = Xml.resolveSerializer(fstream); out.startDocument(null, true); WallpaperData wallpaper; @@ -2929,56 +2924,56 @@ public class WallpaperManagerService extends IWallpaperManager.Stub out.endDocument(); - stream.flush(); // also flushes fstream + fstream.flush(); FileUtils.sync(fstream); - stream.close(); // also closes fstream + fstream.close(); journal.commit(); } catch (IOException e) { - IoUtils.closeQuietly(stream); + IoUtils.closeQuietly(fstream); journal.rollback(); } } - private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper) + private void writeWallpaperAttributes(TypedXmlSerializer out, String tag, + WallpaperData wallpaper) throws IllegalArgumentException, IllegalStateException, IOException { if (DEBUG) { Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId); } final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY); out.startTag(null, tag); - out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId)); - out.attribute(null, "width", Integer.toString(wpdData.mWidth)); - out.attribute(null, "height", Integer.toString(wpdData.mHeight)); + out.attributeInt(null, "id", wallpaper.wallpaperId); + out.attributeInt(null, "width", wpdData.mWidth); + out.attributeInt(null, "height", wpdData.mHeight); - out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left)); - out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top)); - out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right)); - out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom)); + out.attributeInt(null, "cropLeft", wallpaper.cropHint.left); + out.attributeInt(null, "cropTop", wallpaper.cropHint.top); + out.attributeInt(null, "cropRight", wallpaper.cropHint.right); + out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom); if (wpdData.mPadding.left != 0) { - out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left)); + out.attributeInt(null, "paddingLeft", wpdData.mPadding.left); } if (wpdData.mPadding.top != 0) { - out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top)); + out.attributeInt(null, "paddingTop", wpdData.mPadding.top); } if (wpdData.mPadding.right != 0) { - out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right)); + out.attributeInt(null, "paddingRight", wpdData.mPadding.right); } if (wpdData.mPadding.bottom != 0) { - out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom)); + out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom); } if (wallpaper.primaryColors != null) { int colorsCount = wallpaper.primaryColors.getMainColors().size(); - out.attribute(null, "colorsCount", Integer.toString(colorsCount)); + out.attributeInt(null, "colorsCount", colorsCount); if (colorsCount > 0) { for (int i = 0; i < colorsCount; i++) { final Color wc = wallpaper.primaryColors.getMainColors().get(i); - out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb())); + out.attributeInt(null, "colorValue" + i, wc.toArgb()); } } - out.attribute(null, "colorHints", - Integer.toString(wallpaper.primaryColors.getColorHints())); + out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints()); } out.attribute(null, "name", wallpaper.name); @@ -3028,12 +3023,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private int getAttributeInt(XmlPullParser parser, String name, int defValue) { - String value = parser.getAttributeValue(null, name); - if (value == null) { - return defValue; - } - return Integer.parseInt(value); + private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) { + return parser.getAttributeInt(null, name, defValue); } /** @@ -3213,11 +3204,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper, - boolean keepDimensionHints) { - final String idString = parser.getAttributeValue(null, "id"); - if (idString != null) { - final int id = wallpaper.wallpaperId = Integer.parseInt(idString); + private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper, + boolean keepDimensionHints) throws XmlPullParserException { + final int id = parser.getAttributeInt(null, "id", -1); + if (id != -1) { + wallpaper.wallpaperId = id; if (id > mWallpaperId) { mWallpaperId = id; } @@ -3228,8 +3219,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY); if (!keepDimensionHints) { - wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width")); - wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height")); + wpData.mWidth = parser.getAttributeInt(null, "width"); + wpData.mHeight = parser.getAttributeInt(null, "height"); } wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0); wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index b4ca7c5f6ff1..6a50b793de0f 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -425,20 +425,20 @@ class ActivityMetricsLogger { mLastLogTimeSecs = now; mWindowState = WINDOW_STATE_INVALID; - Task stack = mSupervisor.mRootWindowContainer.getTopDisplayFocusedStack(); - if (stack == null) { + Task rootTask = mSupervisor.mRootWindowContainer.getTopDisplayFocusedRootTask(); + if (rootTask == null) { return; } - if (stack.isActivityTypeAssistant()) { + if (rootTask.isActivityTypeAssistant()) { mWindowState = WINDOW_STATE_ASSISTANT; return; } - @WindowingMode int windowingMode = stack.getWindowingMode(); + @WindowingMode int windowingMode = rootTask.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { - stack = mSupervisor.mRootWindowContainer.findStackBehind(stack); - windowingMode = stack.getWindowingMode(); + rootTask = mSupervisor.mRootWindowContainer.findRootTaskBehind(rootTask); + windowingMode = rootTask.getWindowingMode(); } switch (windowingMode) { case WINDOWING_MODE_FULLSCREEN: @@ -456,7 +456,7 @@ class ActivityMetricsLogger { break; default: if (windowingMode != WINDOWING_MODE_UNDEFINED) { - throw new IllegalStateException("Unknown windowing mode for stack=" + stack + throw new IllegalStateException("Unknown windowing mode for task=" + rootTask + " windowingMode=" + windowingMode); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 913c3e580adf..1b8cc082f598 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; -import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX; import static android.app.ActivityOptions.ANIM_CLIP_REVEAL; import static android.app.ActivityOptions.ANIM_CUSTOM; import static android.app.ActivityOptions.ANIM_NONE; @@ -96,18 +95,18 @@ import static android.view.Display.COLOR_MODE_DEFAULT; import static android.view.Display.INVALID_DISPLAY; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static android.view.WindowManager.TRANSIT_CLOSE; +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_TASK_OPEN_BEHIND; +import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.TRANSIT_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 android.view.WindowManager.TransitionOldType; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -183,6 +182,7 @@ import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; +import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; 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; @@ -194,7 +194,6 @@ import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.ActivityState.STARTED; import static com.android.server.wm.Task.ActivityState.STOPPED; import static com.android.server.wm.Task.ActivityState.STOPPING; -import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; @@ -277,6 +276,8 @@ import android.util.Log; import android.util.MergedConfiguration; import android.util.Slog; import android.util.TimeUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.AppTransitionAnimationSpec; import android.view.DisplayCutout; @@ -291,6 +292,7 @@ import android.view.SurfaceControl.Transaction; import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; +import android.view.WindowManager.TransitionOldType; import android.view.animation.Animation; import android.window.WindowContainerToken; @@ -320,9 +322,7 @@ import com.android.server.wm.utils.InsetUtils; import com.google.android.collect.Sets; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.IOException; @@ -1491,13 +1491,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller, + private ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller, int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified, boolean _rootVoiceInteraction, ActivityTaskSupervisor supervisor, - ActivityOptions options, ActivityRecord sourceRecord) { + ActivityOptions options, ActivityRecord sourceRecord, PersistableBundle persistentState, + TaskDescription _taskDescription, long _createTime) { super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true, null /* displayContent */, false /* ownerCanManageAppTokens */); @@ -1652,6 +1653,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mHandoverLaunchDisplayId = options.getLaunchDisplayId(); mLaunchCookie = options.getLaunchCookie(); } + + mPersistentState = persistentState; + taskDescription = _taskDescription; + if (_createTime > 0) { + createTime = _createTime; + } } /** @@ -2550,15 +2557,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return FINISH_RESULT_CANCELLED; } - final Task stack = getRootTask(); - final boolean mayAdjustTop = (isState(RESUMED) || stack.mResumedActivity == null) - && stack.isFocusedStackOnDisplay() + final Task rootTask = getRootTask(); + final boolean mayAdjustTop = (isState(RESUMED) || rootTask.mResumedActivity == null) + && rootTask.isFocusedStackOnDisplay() // Do not adjust focus task because the task will be reused to launch new activity. && !task.isClearingToReuseTask(); final boolean shouldAdjustGlobalFocus = mayAdjustTop // It must be checked before {@link #makeFinishingLocked} is called, because a stack // is not visible if it only contains finishing activities. - && mRootWindowContainer.isTopDisplayFocusedStack(stack); + && mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask); mAtmService.deferWindowLayout(); try { @@ -2623,12 +2630,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Tell window manager to prepare for this one to be removed. setVisibility(false); - if (stack.mPausingActivity == null) { + if (rootTask.mPausingActivity == null) { ProtoLog.v(WM_DEBUG_STATES, "Finish needs to pause: %s", this); if (DEBUG_USER_LEAVING) { Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false"); } - stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, + rootTask.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, null /* resuming */, "finish"); } @@ -2827,7 +2834,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A false /* markFrozenIfConfigChanged */, true /* deferResume */); } if (activityRemoved) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned " @@ -2849,7 +2856,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTaskSupervisor.mFinishingActivities.add(this); } resumeKeyDispatchingLocked(); - return mRootWindowContainer.resumeFocusedStacksTopActivities(); + return mRootWindowContainer.resumeFocusedTasksTopActivities(); } /** @@ -3014,7 +3021,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A removeFromHistory(reason); } - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } /** @@ -5206,7 +5213,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { if (deferRelaunchUntilPaused) { destroyImmediately("stop-config"); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } else { mRootWindowContainer.updatePreviousProcess(this); } @@ -5703,7 +5710,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // First find the real culprit... if this activity has stopped, then the key dispatching // timeout should not be caused by this. if (stopped) { - final Task stack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (stack == null) { return this; } @@ -6091,7 +6098,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) { t.setLayer(leash, getAnimationLayer()); - getDisplayContent().assignStackOrdering(); + getDisplayContent().assignRootTaskOrdering(); } @Override @@ -6372,14 +6379,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void setRequestedOrientation(int requestedOrientation) { - setOrientation(requestedOrientation, mayFreezeScreenLocked()); - mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( - task.mTaskId, requestedOrientation); - } - - private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) { - final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null; - setOrientation(requestedOrientation, binder, this); + setOrientation(requestedOrientation, this); // Push the new configuration to the requested app in case where it's not pushed, e.g. when // the request is handled at task level with letterbox. @@ -6387,6 +6387,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastReportedConfiguration.getMergedConfiguration())) { ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); } + + mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( + task.mTaskId, requestedOrientation); } /* @@ -6403,8 +6406,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return; } - final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null; - if (onDescendantOrientationChanged(freezeToken, this)) { + if (onDescendantOrientationChanged(this)) { // The app is just becoming visible, and the parent Task has updated with the // orientation request. Update the size compat mode. updateSizeCompatMode(); @@ -7081,6 +7083,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } + if (isState(DESTROYED)) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + + "in destroyed state %s", this); + return true; + } + if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check " + "invisible: %s", this); @@ -7464,9 +7472,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A || (info.flags & FLAG_NO_HISTORY) != 0; } - void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { - out.attribute(null, ATTR_ID, String.valueOf(createTime)); - out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid)); + void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException { + out.attributeLong(null, ATTR_ID, createTime); + out.attributeInt(null, ATTR_LAUNCHEDFROMUID, launchedFromUid); if (launchedFromPackage != null) { out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage); } @@ -7476,8 +7484,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (resolvedType != null) { out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType); } - out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified)); - out.attribute(null, ATTR_USERID, String.valueOf(mUserId)); + out.attributeBoolean(null, ATTR_COMPONENTSPECIFIED, componentSpecified); + out.attributeInt(null, ATTR_USERID, mUserId); if (taskDescription != null) { taskDescription.saveToXml(out); @@ -7494,43 +7502,20 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - static ActivityRecord restoreFromXml(XmlPullParser in, + static ActivityRecord restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException { Intent intent = null; PersistableBundle persistentState = null; - int launchedFromUid = 0; - String launchedFromPackage = null; - String launchedFromFeature = null; - String resolvedType = null; - boolean componentSpecified = false; - int userId = 0; - long createTime = -1; + int launchedFromUid = in.getAttributeInt(null, ATTR_LAUNCHEDFROMUID, 0); + String launchedFromPackage = in.getAttributeValue(null, ATTR_LAUNCHEDFROMPACKAGE); + String launchedFromFeature = in.getAttributeValue(null, ATTR_LAUNCHEDFROMFEATURE); + String resolvedType = in.getAttributeValue(null, ATTR_RESOLVEDTYPE); + boolean componentSpecified = in.getAttributeBoolean(null, ATTR_COMPONENTSPECIFIED, false); + int userId = in.getAttributeInt(null, ATTR_USERID, 0); + long createTime = in.getAttributeLong(null, ATTR_ID, -1); final int outerDepth = in.getDepth(); - TaskDescription taskDescription = new TaskDescription(); - for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { - final String attrName = in.getAttributeName(attrNdx); - final String attrValue = in.getAttributeValue(attrNdx); - if (DEBUG) Slog.d(TaskPersister.TAG, - "ActivityRecord: attribute name=" + attrName + " value=" + attrValue); - if (ATTR_ID.equals(attrName)) { - createTime = Long.parseLong(attrValue); - } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) { - launchedFromUid = Integer.parseInt(attrValue); - } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) { - launchedFromPackage = attrValue; - } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) { - launchedFromFeature = attrValue; - } else if (ATTR_RESOLVEDTYPE.equals(attrName)) { - resolvedType = attrValue; - } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) { - componentSpecified = Boolean.parseBoolean(attrValue); - } else if (ATTR_USERID.equals(attrName)) { - userId = Integer.parseInt(attrValue); - } else if (!attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) { - Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName); - } - } + TaskDescription taskDescription = new TaskDescription(); taskDescription.restoreFromXml(in); int event; @@ -7566,18 +7551,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent + " resolvedType=" + resolvedType); } - final ActivityRecord r = new ActivityRecord(service, null /* caller */, - 0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, launchedFromFeature, - intent, resolvedType, aInfo, service.getConfiguration(), null /* resultTo */, - null /* resultWho */, 0 /* reqCode */, componentSpecified, - false /* rootVoiceInteraction */, taskSupervisor, null /* options */, - null /* sourceRecord */); - - r.mPersistentState = persistentState; - r.taskDescription = taskDescription; - r.createTime = createTime; - - return r; + return new ActivityRecord.Builder(service) + .setLaunchedFromUid(launchedFromUid) + .setLaunchedFromPackage(launchedFromPackage) + .setLaunchedFromFeature(launchedFromFeature) + .setIntent(intent) + .setResolvedType(resolvedType) + .setActivityInfo(aInfo) + .setComponentSpecified(componentSpecified) + .setPersistentState(persistentState) + .setTaskDescription(taskDescription) + .setCreateTime(createTime) + .build(); } private static boolean isInVrUiMode(Configuration config) { @@ -8005,4 +7990,138 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } return false; } + + static class Builder { + private final ActivityTaskManagerService mAtmService; + private WindowProcessController mCallerApp; + private int mLaunchedFromPid; + private int mLaunchedFromUid; + private String mLaunchedFromPackage; + private String mLaunchedFromFeature; + private Intent mIntent; + private String mResolvedType; + private ActivityInfo mActivityInfo; + private Configuration mConfiguration; + private ActivityRecord mResultTo; + private String mResultWho; + private int mRequestCode; + private boolean mComponentSpecified; + private boolean mRootVoiceInteraction; + private ActivityOptions mOptions; + private ActivityRecord mSourceRecord; + private PersistableBundle mPersistentState; + private TaskDescription mTaskDescription; + private long mCreateTime; + + Builder(ActivityTaskManagerService service) { + mAtmService = service; + } + + Builder setCaller(@NonNull WindowProcessController caller) { + mCallerApp = caller; + return this; + } + + Builder setLaunchedFromPid(int pid) { + mLaunchedFromPid = pid; + return this; + } + + Builder setLaunchedFromUid(int uid) { + mLaunchedFromUid = uid; + return this; + } + + Builder setLaunchedFromPackage(String fromPackage) { + mLaunchedFromPackage = fromPackage; + return this; + } + + Builder setLaunchedFromFeature(String fromFeature) { + mLaunchedFromFeature = fromFeature; + return this; + } + + Builder setIntent(Intent intent) { + mIntent = intent; + return this; + } + + Builder setResolvedType(String resolvedType) { + mResolvedType = resolvedType; + return this; + } + + Builder setActivityInfo(ActivityInfo activityInfo) { + mActivityInfo = activityInfo; + return this; + } + + Builder setResultTo(ActivityRecord resultTo) { + mResultTo = resultTo; + return this; + } + + Builder setResultWho(String resultWho) { + mResultWho = resultWho; + return this; + } + + Builder setRequestCode(int reqCode) { + mRequestCode = reqCode; + return this; + } + + Builder setComponentSpecified(boolean componentSpecified) { + mComponentSpecified = componentSpecified; + return this; + } + + Builder setRootVoiceInteraction(boolean rootVoiceInteraction) { + mRootVoiceInteraction = rootVoiceInteraction; + return this; + } + + Builder setActivityOptions(ActivityOptions options) { + mOptions = options; + return this; + } + + Builder setConfiguration(Configuration config) { + mConfiguration = config; + return this; + } + + Builder setSourceRecord(ActivityRecord source) { + mSourceRecord = source; + return this; + } + + private Builder setPersistentState(PersistableBundle persistentState) { + mPersistentState = persistentState; + return this; + } + + private Builder setTaskDescription(TaskDescription taskDescription) { + mTaskDescription = taskDescription; + return this; + } + + private Builder setCreateTime(long createTime) { + mCreateTime = createTime; + return this; + } + + ActivityRecord build() { + if (mConfiguration == null) { + mConfiguration = mAtmService.getConfiguration(); + } + return new ActivityRecord(mAtmService, mCallerApp, mLaunchedFromPid, + mLaunchedFromUid, mLaunchedFromPackage, mLaunchedFromFeature, mIntent, + mResolvedType, mActivityInfo, mConfiguration, mResultTo, mResultWho, + mRequestCode, mComponentSpecified, mRootVoiceInteraction, + mAtmService.mTaskSupervisor, mOptions, mSourceRecord, mPersistentState, + mTaskDescription, mCreateTime); + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index b88b54ef08f5..1ff3a3fb1d35 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -664,7 +664,7 @@ class ActivityStarter { synchronized (mService.mGlobalLock) { final boolean globalConfigWillChange = mRequest.globalConfig != null && mService.getGlobalConfiguration().diff(mRequest.globalConfig) != 0; - final Task stack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (stack != null) { stack.mConfigWillChange = globalConfigWillChange; } @@ -900,7 +900,7 @@ class ActivityStarter { ActivityRecord sourceRecord = null; ActivityRecord resultRecord = null; if (resultTo != null) { - sourceRecord = mRootWindowContainer.isInAnyStack(resultTo); + sourceRecord = mRootWindowContainer.isInAnyTask(resultTo); if (DEBUG_RESULTS) { Slog.v(TAG_RESULTS, "Will send result to " + resultTo + " " + sourceRecord); } @@ -1135,7 +1135,7 @@ class ActivityStarter { if (DEBUG_PERMISSIONS_REVIEW) { final Task focusedStack = - mRootWindowContainer.getTopDisplayFocusedStack(); + mRootWindowContainer.getTopDisplayFocusedRootTask(); Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false) + "} from uid " + callingUid + " on display " + (focusedStack == null ? DEFAULT_DISPLAY @@ -1161,12 +1161,25 @@ class ActivityStarter { aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, null /*profilerInfo*/); } + final ActivityRecord r = new ActivityRecord.Builder(mService) + .setCaller(callerApp) + .setLaunchedFromPid(callingPid) + .setLaunchedFromUid(callingUid) + .setLaunchedFromPackage(callingPackage) + .setLaunchedFromFeature(callingFeatureId) + .setIntent(intent) + .setResolvedType(resolvedType) + .setActivityInfo(aInfo) + .setConfiguration(mService.getGlobalConfiguration()) + .setResultTo(resultRecord) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setComponentSpecified(request.componentSpecified) + .setRootVoiceInteraction(voiceSession != null) + .setActivityOptions(checkedOptions) + .setSourceRecord(sourceRecord) + .build(); - final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, - callingPackage, callingFeatureId, intent, resolvedType, aInfo, - mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode, - request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions, - sourceRecord); mLastStartActivityRecord = r; if (r.appTimeTracker == null && sourceRecord != null) { @@ -1175,7 +1188,7 @@ class ActivityStarter { r.appTimeTracker = sourceRecord.appTimeTracker; } - final Task stack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task stack = mRootWindowContainer.getTopDisplayFocusedRootTask(); // If we are starting an activity that is not from the same uid as the currently resumed // one, check whether app switches are allowed. @@ -1718,7 +1731,7 @@ class ActivityStarter { // If the activity being launched is the same as the one currently at the top, then // we need to check if it should only be launched once. - final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topStack != null) { startResult = deliverToCurrentTopIfNeeded(topStack, intentGrants); if (startResult != START_SUCCESS) { @@ -1806,14 +1819,14 @@ class ActivityStarter { // task stack to be focusable, then ensure that we now update the focused stack // accordingly. if (mTargetStack.isTopActivityFocusable() - && !mRootWindowContainer.isTopDisplayFocusedStack(mTargetStack)) { + && !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetStack)) { mTargetStack.moveToFront("startActivityInner"); } - mRootWindowContainer.resumeFocusedStacksTopActivities( + mRootWindowContainer.resumeFocusedTasksTopActivities( mTargetStack, mStartActivity, mOptions); } } - mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack); + mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetStack); // Update the recent tasks list immediately when the activity starts mSupervisor.mRecentTasks.add(mStartActivity.getTask()); @@ -1849,7 +1862,7 @@ class ActivityStarter { private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord, Task targetTask) { final Task sourceStack = mSourceStack != null ? mSourceStack - : mRootWindowContainer.getTopDisplayFocusedStack(); + : mRootWindowContainer.getTopDisplayFocusedRootTask(); if (sourceStack != null && sourceStack.inSplitScreenWindowingMode() && (mOptions == null || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) { @@ -2048,7 +2061,7 @@ class ActivityStarter { // For paranoia, make sure we have correctly resumed the top activity. topStack.mLastPausedActivity = null; if (mDoResume) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } ActivityOptions.abort(mOptions); if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { @@ -2347,7 +2360,7 @@ class ActivityStarter { if ((startFlags & START_FLAG_ONLY_IF_NEEDED) != 0) { ActivityRecord checkedCaller = sourceRecord; if (checkedCaller == null) { - Task topFocusedStack = mRootWindowContainer.getTopDisplayFocusedStack(); + Task topFocusedStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topFocusedStack != null) { checkedCaller = topFocusedStack.topRunningNonDelayedActivityLocked(mNotTop); } @@ -2567,7 +2580,7 @@ class ActivityStarter { // to the front if the caller is not itself in the front. final boolean differentTopTask; if (mTargetStack.getDisplayArea() == mPreferredTaskDisplayArea) { - final Task focusStack = mTargetStack.mDisplayContent.getFocusedStack(); + final Task focusStack = mTargetStack.mDisplayContent.getFocusedRootTask(); final ActivityRecord curTop = (focusStack == null) ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop); final Task topTask = curTop != null ? curTop.getTask() : null; @@ -2634,11 +2647,11 @@ class ActivityStarter { if (next != null) { next.setCurrentLaunchCanTurnScreenOn(true); } - mRootWindowContainer.resumeFocusedStacksTopActivities(mTargetStack, null, mOptions); + mRootWindowContainer.resumeFocusedTasksTopActivities(mTargetStack, null, mOptions); } else { ActivityOptions.abort(mOptions); } - mRootWindowContainer.updateUserStack(mStartActivity.mUserId, mTargetStack); + mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetStack); } private void setNewTask(Task taskToAffiliate) { @@ -2713,7 +2726,7 @@ class ActivityStarter { final boolean onTop = (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind; - return mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams, + return mRootWindowContainer.getLaunchRootTask(r, aOptions, task, onTop, mLaunchParams, mRequest.realCallingPid, mRequest.realCallingUid); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index e12dc2bd56a8..3710120a7934 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1156,7 +1156,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); if (stack != null && stack.mResumedActivity != null && stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) { mAppSwitchesAllowedTime = 0; @@ -1470,7 +1470,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { sourceToken = resultTo; } - sourceRecord = mRootWindowContainer.isInAnyStack(sourceToken); + sourceRecord = mRootWindowContainer.isInAnyTask(sourceToken); if (sourceRecord == null) { throw new SecurityException("Called with bad activity token: " + sourceToken); } @@ -2039,7 +2039,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean isTopActivityImmersive() { enforceNotIsolatedCaller("isTopActivityImmersive"); synchronized (mGlobalLock) { - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); if (topFocusedStack == null) { return false; } @@ -2074,7 +2074,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public int getFrontActivityScreenCompatMode() { enforceNotIsolatedCaller("getFrontActivityScreenCompatMode"); synchronized (mGlobalLock) { - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); final ActivityRecord r = stack != null ? stack.topRunningActivity() : null; if (r == null) { return ActivityManager.COMPAT_MODE_UNKNOWN; @@ -2089,7 +2089,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "setFrontActivityScreenCompatMode"); ApplicationInfo ai; synchronized (mGlobalLock) { - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); final ActivityRecord r = stack != null ? stack.topRunningActivity() : null; if (r == null) { Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); @@ -2165,7 +2165,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void notifyActivityDrawn(IBinder token) { if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token); synchronized (mGlobalLock) { - ActivityRecord r = mRootWindowContainer.isInAnyStack(token); + ActivityRecord r = mRootWindowContainer.isInAnyTask(token); if (r != null) { r.getRootTask().notifyActivityDrawnLocked(r); } @@ -2201,7 +2201,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - Task focusedStack = getTopDisplayFocusedStack(); + Task focusedStack = getTopDisplayFocusedRootTask(); if (focusedStack != null) { return mRootWindowContainer.getRootTaskInfo(focusedStack.mTaskId); } @@ -2219,14 +2219,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long callingId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - final Task task = mRootWindowContainer.getStack(taskId); + final Task task = mRootWindowContainer.getRootTask(taskId); if (task == null) { Slog.w(TAG, "setFocusedRootTask: No task with id=" + taskId); return; } final ActivityRecord r = task.topRunningActivity(); if (r != null && r.moveFocusableActivityToTop("setFocusedRootTask")) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } } finally { @@ -2248,7 +2248,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final ActivityRecord r = task.topRunningActivityLocked(); if (r != null && r.moveFocusableActivityToTop("setFocusedTask")) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } } finally { @@ -2508,7 +2508,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { final long origId = Binder.clearCallingIdentity(); try { - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); if (topFocusedStack != null) { topFocusedStack.unhandledBackLocked(); } @@ -2738,9 +2738,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - @Override - public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { - return getFilteredTasks(maxNum, false /* filterForVisibleRecents */); + List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) { + return getTasks(maxNum, false /* filterForVisibleRecents */); } /** @@ -2748,7 +2747,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * be visible in the recent task list in systemui */ @Override - public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, + public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); @@ -2821,7 +2820,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { ProtoLog.d(WM_DEBUG_TASKS, "moveTaskToRootTask: moving task=%d to " + "rootTaskId=%d toTop=%b", taskId, rootTaskId, toTop); - final Task rootTask = mRootWindowContainer.getStack(rootTaskId); + final Task rootTask = mRootWindowContainer.getRootTask(rootTaskId); if (rootTask == null) { throw new IllegalStateException( "moveTaskToRootTask: No rootTask for rootTaskId=" + rootTaskId); @@ -3089,8 +3088,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return; } - final Task stack = mRootWindowContainer.getTopDisplayFocusedStack(); - if (stack == null || task != stack.getTopMostTask()) { + final Task rootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); + if (rootTask == null || task != rootTask.getTopMostTask()) { throw new IllegalArgumentException("Invalid task, not in foreground"); } @@ -3371,7 +3370,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final Task stack = r.getRootTask(); - final Task task = stack.getDisplayArea().createStack(stack.getWindowingMode(), + final Task task = stack.getDisplayArea().createRootTask(stack.getWindowingMode(), stack.getActivityType(), !ON_TOP, ainfo, intent, false /* createdByOrganizer */); @@ -3534,7 +3533,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { ProtoLog.d(WM_DEBUG_TASKS, "moveRootTaskToDisplay: moving taskId=%d to " + "displayId=%d", taskId, displayId); - mRootWindowContainer.moveStackToDisplay(taskId, displayId, ON_TOP); + mRootWindowContainer.moveRootTaskToDisplay(taskId, displayId, ON_TOP); } finally { Binder.restoreCallingIdentity(ident); } @@ -3742,7 +3741,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "enqueueAssistContext()"); synchronized (mGlobalLock) { - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); ActivityRecord activity = stack != null ? stack.getTopNonFinishingActivity() : null; if (activity == null) { Slog.w(TAG, "getAssistContextExtras failed: no top activity"); @@ -3871,7 +3870,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean isAssistDataAllowedOnCurrentActivity() { int userId; synchronized (mGlobalLock) { - final Task focusedStack = getTopDisplayFocusedStack(); + final Task focusedStack = getTopDisplayFocusedRootTask(); if (focusedStack == null || focusedStack.isActivityTypeAssistant()) { return false; } @@ -3891,7 +3890,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { ActivityRecord caller = ActivityRecord.forTokenLocked(token); - ActivityRecord top = getTopDisplayFocusedStack().getTopNonFinishingActivity(); + ActivityRecord top = getTopDisplayFocusedRootTask().getTopNonFinishingActivity(); if (top != caller) { Slog.w(TAG, "showAssistFromActivity failed: caller " + caller + " is not current top " + top); @@ -4070,7 +4069,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long ident = Binder.clearCallingIdentity(); try { - return mRootWindowContainer.moveTopStackActivityToPinnedRootTask(rootTaskId); + return mRootWindowContainer.moveTopRootTaskActivityToPinnedRootTask(rootTaskId); } finally { Binder.restoreCallingIdentity(ident); } @@ -4114,7 +4113,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.setPictureInPictureParams(params); final float aspectRatio = r.pictureInPictureArgs.getAspectRatio(); final List<RemoteAction> actions = r.pictureInPictureArgs.getActions(); - mRootWindowContainer.moveActivityToPinnedStack( + mRootWindowContainer.moveActivityToPinnedRootTask( r, "enterPictureInPictureMode"); final Task stack = r.getRootTask(); stack.setPictureInPictureAspectRatio(aspectRatio); @@ -4346,7 +4345,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { Slog.i(TAG, "Activity tried to startLocalVoiceInteraction"); synchronized (mGlobalLock) { - ActivityRecord activity = getTopDisplayFocusedStack().getTopNonFinishingActivity(); + ActivityRecord activity = getTopDisplayFocusedRootTask().getTopNonFinishingActivity(); if (ActivityRecord.forTokenLocked(callingActivity) != activity) { throw new SecurityException("Only focused activity can call startVoiceInteraction"); } @@ -4750,7 +4749,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) { Slog.i(TAG, "Moving " + r.shortComponentName + " from display " + r.getDisplayId() + " to main display for VR"); - mRootWindowContainer.moveStackToDisplay( + mRootWindowContainer.moveRootTaskToDisplay( r.getRootTaskId(), DEFAULT_DISPLAY, true /* toTop */); } mH.post(() -> { @@ -4814,8 +4813,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } - Task getTopDisplayFocusedStack() { - return mRootWindowContainer.getTopDisplayFocusedStack(); + Task getTopDisplayFocusedRootTask() { + return mRootWindowContainer.getTopDisplayFocusedRootTask(); } /** Pokes the task persister. */ @@ -5828,7 +5827,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** Applies latest configuration and/or visibility updates if needed. */ boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) { boolean kept = true; - final Task mainStack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task mainStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); // mainStack is null during startup. if (mainStack != null) { if (changes != 0 && starting == null) { @@ -6083,8 +6082,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { static final int REPORT_TIME_TRACKER_MSG = 1; - static final int FIRST_ACTIVITY_STACK_MSG = 100; - static final int FIRST_SUPERVISOR_STACK_MSG = 200; + static final int FIRST_ACTIVITY_TASK_MSG = 100; + static final int FIRST_SUPERVISOR_TASK_MSG = 200; H(Looper looper) { super(looper); @@ -6293,7 +6292,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { "setFocusedActivity: No activity record matching token=" + token); } if (r.moveFocusableActivityToTop("setFocusedActivity")) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } } @@ -6797,7 +6796,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (!restarting && hasVisibleActivities) { deferWindowLayout(); try { - if (!mRootWindowContainer.resumeFocusedStacksTopActivities()) { + if (!mRootWindowContainer.resumeFocusedTasksTopActivities()) { // If there was nothing to resume, and we are not already restarting // this process, but there is a visible activity that is hosted by the // process...then make sure all visible activities are running, taking @@ -6850,7 +6849,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (mRootWindowContainer.finishDisabledPackageActivities( packageName, disabledClasses, true /* doit */, false /* evenPersistent */, userId, false /* onlyRemoveNoProcess */) && booted) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); mTaskSupervisor.scheduleIdle(); } @@ -6880,7 +6879,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void resumeTopActivities(boolean scheduleIdle) { synchronized (mGlobalLock) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); if (scheduleIdle) { mTaskSupervisor.scheduleIdle(); } @@ -7057,7 +7056,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mRootWindowContainer.dumpDisplayConfigs(pw, " "); } if (dumpAll) { - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); if (dumpPackage == null && topFocusedStack != null) { pw.println(" mConfigWillChange: " + topFocusedStack.mConfigWillChange); } @@ -7140,7 +7139,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { synchronized (mGlobalLock) { if (dumpPackage == null) { getGlobalConfiguration().dumpDebug(proto, GLOBAL_CONFIGURATION); - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); if (topFocusedStack != null) { proto.write(CONFIG_WILL_CHANGE, topFocusedStack.mConfigWillChange); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index d0c26af82d99..73d99724c65f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -63,7 +63,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; -import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_STACK_MSG; +import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_SUPERVISOR_TASK_MSG; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_ALLOWLISTED; import static com.android.server.wm.LockTaskController.LOCK_TASK_AUTH_LAUNCHABLE; @@ -176,18 +176,18 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** How long we wait until giving up on the activity telling us it released the top state. */ private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT = 500; - private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG; - private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_STACK_MSG + 1; - private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2; - private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3; - private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4; - private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_STACK_MSG + 5; - private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12; - private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13; - private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14; - private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15; - private static final int START_HOME_MSG = FIRST_SUPERVISOR_STACK_MSG + 16; - private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 17; + private static final int IDLE_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG; + private static final int IDLE_NOW_MSG = FIRST_SUPERVISOR_TASK_MSG + 1; + private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_TASK_MSG + 2; + private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 3; + private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 4; + private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_TASK_MSG + 5; + private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_TASK_MSG + 12; + private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 13; + private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_TASK_MSG + 14; + private static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_TASK_MSG + 15; + private static final int START_HOME_MSG = FIRST_SUPERVISOR_TASK_MSG + 16; + private static final int TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG = FIRST_SUPERVISOR_TASK_MSG + 17; // Used to indicate that windows of activities should be preserved during the resize. static final boolean PRESERVE_WINDOWS = true; @@ -202,8 +202,8 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Used to indicate that a task is removed it should also be removed from recents. static final boolean REMOVE_FROM_RECENTS = true; - /** True if the docked stack is currently being resized. */ - private boolean mDockedStackResizing; + /** True if the docked root task is currently being resized. */ + private boolean mDockedRootTaskResizing; // Activity actions an app cannot start if it uses a permission which is not granted. private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION = @@ -307,7 +307,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** The target stack bounds for the picture-in-picture mode changed that we need to report to * the application */ - private Rect mPipModeChangedTargetStackBounds; + private Rect mPipModeChangedTargetRootTaskBounds; /** Used on user changes */ final ArrayList<UserState> mStartingUsers = new ArrayList<>(); @@ -386,17 +386,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ActivityRecord r; final ActivityRecord sourceRecord; final int startFlags; - final Task stack; + final Task rootTask; final WindowProcessController callerApp; final NeededUriGrants intentGrants; PendingActivityLaunch(ActivityRecord r, ActivityRecord sourceRecord, - int startFlags, Task stack, WindowProcessController callerApp, + int startFlags, Task rootTask, WindowProcessController callerApp, NeededUriGrants intentGrants) { this.r = r; this.sourceRecord = sourceRecord; this.startFlags = startFlags; - this.stack = stack; + this.rootTask = rootTask; this.callerApp = callerApp; this.intentGrants = intentGrants; } @@ -501,11 +501,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { getKeyguardController().setWindowManager(wm); } - void moveRecentsStackToFront(String reason) { - final Task recentsStack = mRootWindowContainer.getDefaultTaskDisplayArea() - .getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); - if (recentsStack != null) { - recentsStack.moveToFront(reason); + void moveRecentsRootTaskToFront(String reason) { + final Task recentsRootTask = mRootWindowContainer.getDefaultTaskDisplayArea() + .getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); + if (recentsRootTask != null) { + recentsRootTask.moveToFront(reason); } } @@ -944,7 +944,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // launching the initial activity (that is, home), so that it can have // a chance to initialize itself while in the background, making the // switch back to it faster and look better. - if (mRootWindowContainer.isTopDisplayFocusedStack(rootTask)) { + if (mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask)) { mService.getActivityStartController().startSetupActivity(); } @@ -1376,10 +1376,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { task.setBounds(bounds); Task stack = - mRootWindowContainer.getLaunchStack(null, options, task, ON_TOP); + mRootWindowContainer.getLaunchRootTask(null, options, task, ON_TOP); if (stack != currentStack) { - moveHomeStackToFrontIfNeeded(flags, stack.getDisplayArea(), reason); + moveHomeRootTaskToFrontIfNeeded(flags, stack.getDisplayArea(), reason); task.reparent(stack, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT, !ANIMATE, DEFER_RESUME, reason); currentStack = stack; @@ -1398,7 +1398,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } if (!reparented) { - moveHomeStackToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason); + moveHomeRootTaskToFrontIfNeeded(flags, currentStack.getDisplayArea(), reason); } final ActivityRecord r = task.getTopNonFinishingActivity(); @@ -1412,16 +1412,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mRootWindowContainer.getDefaultTaskDisplayArea(), currentStack, forceNonResizeable); } - private void moveHomeStackToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea, + private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea, String reason) { - final Task focusedStack = taskDisplayArea.getFocusedStack(); + final Task focusedRootTask = taskDisplayArea.getFocusedRootTask(); if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) - || (focusedStack != null && focusedStack.isActivityTypeRecents())) { + || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) { // We move home stack to front when we are on a fullscreen display area and caller has // requested the home activity to move with it. Or the previous stack is recents. - taskDisplayArea.moveHomeStackToFront(reason); + taskDisplayArea.moveHomeRootTaskToFront(reason); } } @@ -1441,15 +1441,15 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } void setSplitScreenResizing(boolean resizing) { - if (resizing == mDockedStackResizing) { + if (resizing == mDockedRootTaskResizing) { return; } - mDockedStackResizing = resizing; + mDockedRootTaskResizing = resizing; mWindowManager.setDockedStackResizing(resizing); } - private void removePinnedStackInSurfaceTransaction(Task stack) { + private void removePinnedRootTaskInSurfaceTransaction(Task rootTask) { /** * Workaround: Force-stop all the activities in the pinned stack before we reparent them * to the fullscreen stack. This is to guarantee that when we are removing a stack, @@ -1459,9 +1459,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * marked invisible as well and added to the stopping list. After which we process the * stopping list by handling the idle. */ - stack.cancelAnimation(); - stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); - stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + rootTask.cancelAnimation(); + rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + rootTask.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); activityIdleInternal(null /* idleActivity */, false /* fromTimeout */, true /* processPausingActivities */, null /* configuration */); @@ -1471,17 +1471,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mService.deferWindowLayout(); try { - stack.setWindowingMode(WINDOWING_MODE_UNDEFINED); - if (stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - stack.setBounds(null); + rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED); + if (rootTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + rootTask.setBounds(null); } - toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(stack); + toDisplay.getDefaultTaskDisplayArea().positionTaskBehindHome(rootTask); // Follow on the workaround: activities are kept force hidden till the new windowing // mode is set. - stack.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); + rootTask.setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } finally { mService.continueWindowLayout(); } @@ -1489,7 +1489,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { private void removeRootTaskInSurfaceTransaction(Task rootTask) { if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) { - removePinnedStackInSurfaceTransaction(rootTask); + removePinnedRootTaskInSurfaceTransaction(rootTask); } else { final PooledConsumer c = PooledLambda.obtainConsumer( ActivityTaskSupervisor::processRemoveTask, this, PooledLambda.__(Task.class)); @@ -1627,7 +1627,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ boolean restoreRecentTaskLocked(Task task, ActivityOptions aOptions, boolean onTop) { final Task stack = - mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop); + mRootWindowContainer.getLaunchRootTask(null, aOptions, task, onTop); final WindowContainer parent = task.getParent(); if (parent == stack || task == stack) { @@ -1668,7 +1668,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * the various checks on tasks that are going to be reparented from one stack to another. */ // TODO: Look into changing users to this method to DisplayContent.resolveWindowingMode() - Task getReparentTargetStack(Task task, Task stack, boolean toTop) { + Task getReparentTargetRootTask(Task task, Task stack, boolean toTop) { final Task prevStack = task.getRootTask(); final int rootTaskId = stack.mTaskId; final boolean inMultiWindowMode = stack.inMultiWindowMode(); @@ -1709,7 +1709,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (prevStack != null) { return prevStack; } - stack = stack.getDisplayArea().createStack( + stack = stack.getDisplayArea().createRootTask( WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), toTop); } return stack; @@ -1739,7 +1739,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { boolean timedout = false; final long endTime = System.currentTimeMillis() + timeout; while (true) { - if (!mRootWindowContainer.putStacksToSleep( + if (!mRootWindowContainer.putTasksToSleep( true /* allowDelay */, true /* shuttingDown */)) { long timeRemaining = endTime - System.currentTimeMillis(); if (timeRemaining > 0) { @@ -1776,7 +1776,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return; } - if (!mRootWindowContainer.putStacksToSleep( + if (!mRootWindowContainer.putTasksToSleep( allowDelay, false /* shuttingDown */)) { return; } @@ -1931,7 +1931,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mService.getLockTaskController().dump(pw, prefix); pw.print(prefix); pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser); - pw.println(prefix + "mUserStackInFront=" + mRootWindowContainer.mUserStackInFront); + pw.println(prefix + "mUserRootTaskInFront=" + mRootWindowContainer.mUserRootTaskInFront); pw.println(prefix + "mVisibilityTransactionDepth=" + mVisibilityTransactionDepth); if (!mWaitingForActivityVisible.isEmpty()) { pw.println(prefix + "mWaitingForActivityVisible="); @@ -2058,7 +2058,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ void updateTopResumedActivityIfNeeded() { final ActivityRecord prevTopActivity = mTopResumedActivity; - final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topStack == null || topStack.mResumedActivity == prevTopActivity) { if (mService.isSleepingLocked()) { // There won't be a next resumed activity. The top process should still be updated @@ -2242,7 +2242,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } } - void logStackState() { + void logRootTaskState() { mActivityMetricsLogger.logWindowState(); } @@ -2281,7 +2281,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { task.forAllActivities(c); c.recycle(); - mPipModeChangedTargetStackBounds = targetStackBounds; + mPipModeChangedTargetRootTaskBounds = targetStackBounds; if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) { mHandler.sendEmptyMessage(REPORT_PIP_MODE_CHANGED_MSG); @@ -2436,7 +2436,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { case REPORT_PIP_MODE_CHANGED_MSG: { for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) { final ActivityRecord r = mPipModeChangedActivities.remove(i); - r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds, + r.updatePictureInPictureMode(mPipModeChangedTargetRootTaskBounds, false /* forceUpdate */); } } break; @@ -2452,7 +2452,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */); } break; case RESUME_TOP_ACTIVITY_MSG: { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } break; case SLEEP_TIMEOUT_MSG: { if (mService.isSleepingOrShuttingDownLocked()) { @@ -2542,7 +2542,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // from whatever is started from the recents activity, so move the home stack // forward. // TODO (b/115289124): Multi-display supports for recents. - mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeStackToFront( + mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront( "startActivityFromRecents"); } @@ -2570,6 +2570,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mService.getActivityStartController().postStartActivityProcessingForLastStarter( task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT, task.getRootTask()); + + // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume + // app switching here also. + mService.resumeAppSwitches(); + return ActivityManager.START_TASK_TO_FRONT; } callingPackage = task.mCallingPackage; diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java index d40dea299ff6..55200b566979 100644 --- a/services/core/java/com/android/server/wm/AppWarnings.java +++ b/services/core/java/com/android/server/wm/AppWarnings.java @@ -492,7 +492,7 @@ class AppWarnings { } out.startTag(null, "package"); out.attribute(null, "name", pkg); - out.attribute(null, "flags", Integer.toString(mode)); + out.attributeInt(null, "flags", mode); out.endTag(null, "package"); } diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index 89c5f62a252a..c22bd20da042 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -17,10 +17,10 @@ package com.android.server.wm; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; -import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import android.app.ActivityManager; import android.app.AppGlobals; @@ -43,7 +43,6 @@ import com.android.internal.protolog.common.ProtoLog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileInputStream; @@ -113,14 +112,7 @@ public final class CompatModePackages { if ("pkg".equals(tagName)) { String pkg = parser.getAttributeValue(null, "name"); if (pkg != null) { - String mode = parser.getAttributeValue(null, "mode"); - int modeInt = 0; - if (mode != null) { - try { - modeInt = Integer.parseInt(mode); - } catch (NumberFormatException e) { - } - } + int modeInt = parser.getAttributeInt(null, "mode", 0); mPackages.put(pkg, modeInt); } } @@ -321,7 +313,7 @@ public final class CompatModePackages { scheduleWrite(); - final Task stack = mService.getTopDisplayFocusedStack(); + final Task stack = mService.getTopDisplayFocusedRootTask(); ActivityRecord starting = stack.restartPackage(packageName); // Tell all processes that loaded this package about the change. @@ -396,7 +388,7 @@ public final class CompatModePackages { } out.startTag(null, "pkg"); out.attribute(null, "name", pkg); - out.attribute(null, "mode", Integer.toString(mode)); + out.attributeInt(null, "mode", mode); out.endTag(null, "pkg"); } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 60a62dc0b64c..36a1ef9f49b4 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -182,6 +182,11 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { // writing to proto (which has significant cost if we write a lot of empty configurations). mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); mRequestedOverrideConfiguration.setTo(overrideConfiguration); + final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds(); + if (mHasOverrideConfiguration && providesMaxBounds() + && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) { + mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds); + } // Update full configuration of this container and all its children. final ConfigurationContainer parent = getParent(); onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); @@ -341,9 +346,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration()); mRequestsTmpConfig.windowConfiguration.setBounds(bounds); - if (overrideMaxBounds) { - mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds); - } onRequestedOverrideConfigurationChanged(mRequestsTmpConfig); return boundsChange; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 48e030050b07..a4ac16f2ec77 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -33,7 +33,6 @@ import static com.android.server.wm.WindowContainerChildProto.DISPLAY_AREA; import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.util.proto.ProtoOutputStream; import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; @@ -151,12 +150,11 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override - boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { // If this is set to ignore the orientation request, we don't propagate descendant // orientation request. return !mIgnoreOrientationRequest - && super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); + && super.onDescendantOrientationChanged(requestingContainer); } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ccc85f834bc1..1d2cd0a0a350 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -55,6 +55,8 @@ import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; @@ -73,8 +75,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; @@ -93,7 +93,6 @@ import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_C import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.DisplayContentProto.APP_TRANSITION; -import static com.android.server.wm.DisplayContentProto.IME_POLICY; import static com.android.server.wm.DisplayContentProto.CLOSING_APPS; import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; @@ -105,6 +104,7 @@ import static com.android.server.wm.DisplayContentProto.FOCUSED_APP; import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID; import static com.android.server.wm.DisplayContentProto.ID; import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER; +import static com.android.server.wm.DisplayContentProto.IME_POLICY; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET; import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET; @@ -559,6 +559,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ InsetsControlTarget mInputMethodControlTarget; + /** The surface parent of the IME container. */ + private SurfaceControl mInputMethodSurfaceParent; + /** If true hold off on modifying the animation layer of mInputMethodTarget */ boolean mInputMethodTargetWaitingAnim; @@ -1300,10 +1303,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } @Override - boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { final Configuration config = updateOrientation( - getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */); + getRequestedOverrideConfiguration(), requestingContainer, false /* forceUpdate */); // If display rotation class tells us that it doesn't consider app requested orientation, // this display won't rotate just because of an app changes its requested orientation. Thus // it indicates that this display chooses not to handle this request. @@ -1318,7 +1320,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp false /* deferResume */, null /* result */); activityRecord.frozenBeforeDestroy = true; if (!kept) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } else { // We have a new configuration to push so we need to update ATMS for now. @@ -1355,11 +1357,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @param currentConfig The current requested override configuration (it is usually set from * the last {@link #sendNewConfiguration}) of the display. It is used to * check if the configuration container has the latest state. - * @param freezeDisplayToken Freeze the app window token if the orientation is changed. + * @param freezeDisplayWindow Freeze the app window if the orientation is changed. * @param forceUpdate See {@link DisplayRotation#updateRotationUnchecked(boolean)} */ - Configuration updateOrientation(Configuration currentConfig, IBinder freezeDisplayToken, - boolean forceUpdate) { + Configuration updateOrientation(Configuration currentConfig, + WindowContainer freezeDisplayWindow, boolean forceUpdate) { if (!mDisplayReady) { return null; } @@ -1368,9 +1370,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (updateOrientation(forceUpdate)) { // If we changed the orientation but mOrientationChangeComplete is already true, // we used seamless rotation, and we don't need to freeze the screen. - if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) { - final ActivityRecord activity = getActivityRecord(freezeDisplayToken); - if (activity != null) { + if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) { + final ActivityRecord activity = freezeDisplayWindow.asActivityRecord(); + if (activity != null && activity.mayFreezeScreenLocked()) { activity.startFreezingScreen(); } } @@ -1677,6 +1679,28 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } + void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) { + if (mFixedRotationLaunchingApp != null) { + // The insets state of fixed rotation app is a rotated copy. Make sure the visibilities + // of insets sources are consistent with the latest state. + final InsetsState rotatedState = + mFixedRotationLaunchingApp.getFixedRotationTransformInsetsState(); + if (rotatedState != null) { + final InsetsState state = mInsetsStateController.getRawInsetsState(); + for (int i = 0; i < InsetsState.SIZE; i++) { + final InsetsSource source = state.peekSource(i); + if (source != null) { + rotatedState.setSourceVisible(i, source.isVisible()); + } + } + } + } + forAllWindows(dispatchInsetsChanged, true /* traverseTopToBottom */); + if (mRemoteInsetsControlTarget != null) { + mRemoteInsetsControlTarget.notifyInsetsChanged(); + } + } + /** * Update rotation of the display. * @@ -2219,26 +2243,26 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * activity type. Null is no compatible stack on the display. */ @Nullable - Task getStack(int windowingMode, int activityType) { + Task getRootTask(int windowingMode, int activityType) { return getItemFromTaskDisplayAreas(taskDisplayArea -> - taskDisplayArea.getStack(windowingMode, activityType)); + taskDisplayArea.getRootTask(windowingMode, activityType)); } @Nullable - Task getStack(int rootTaskId) { + Task getRootTask(int rootTaskId) { return getItemFromTaskDisplayAreas(taskDisplayArea -> - taskDisplayArea.getStack(rootTaskId)); + taskDisplayArea.getRootTask(rootTaskId)); } - protected int getStackCount() { + protected int getRootTaskCount() { return reduceOnAllTaskDisplayAreas((taskDisplayArea, count) -> - count + taskDisplayArea.getStackCount(), 0 /* initValue */); + count + taskDisplayArea.getRootTaskCount(), 0 /* initValue */); } @VisibleForTesting @Nullable - Task getTopStack() { - return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopStack); + Task getTopRootTask() { + return getItemFromTaskDisplayAreas(TaskDisplayArea::getTopRootTask); } /** @@ -2902,7 +2926,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mClosingApps.valueAt(i).writeIdentifierToProto(proto, CLOSING_APPS); } - final Task focusedStack = getFocusedStack(); + final Task focusedStack = getFocusedRootTask(); if (focusedStack != null) { proto.write(FOCUSED_ROOT_TASK_ID, focusedStack.getRootTaskId()); final ActivityRecord focusedActivity = focusedStack.getDisplayArea() @@ -2946,7 +2970,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp public void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); - pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getStackCount()); + pw.println("Display: mDisplayId=" + mDisplayId + " stacks=" + getRootTaskCount()); final String subPrefix = " " + prefix; pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x"); pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity); @@ -3044,13 +3068,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp pw.println(prefix + "splitScreenPrimaryStack=" + splitScreenPrimaryStack.getName()); } // TODO: Support recents on non-default task containers - final Task recentsStack = getDefaultTaskDisplayArea().getStack( + final Task recentsStack = getDefaultTaskDisplayArea().getRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS); if (recentsStack != null) { pw.println(prefix + "recentsStack=" + recentsStack.getName()); } final Task dreamStack = - getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM); + getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM); if (dreamStack != null) { pw.println(prefix + "dreamStack=" + dreamStack.getName()); } @@ -3610,7 +3634,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target); mInputMethodTarget = target; mInputMethodTargetWaitingAnim = targetWaitingAnim; - assignWindowLayers(true /* setLayoutNeeded */); + + // 1. Reparent the IME container window to the target root DA to get the correct bounds and + // config. (Only happens when the target window is in a different root DA) if (target != null) { RootDisplayArea targetRoot = target.getRootDisplayArea(); if (targetRoot != null) { @@ -3619,7 +3645,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp targetRoot.placeImeContainer(mImeWindowsContainers); } } + // 2. Reparent the IME container surface to either the input target app, or the IME window + // parent. updateImeParent(); + // 3. Assign window layers based on the IME surface parent to make sure it is on top of the + // app. + assignWindowLayers(true /* setLayoutNeeded */); + // 4. Update the IME control target to apply any inset change and animation. updateImeControlTarget(); } @@ -3649,7 +3681,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void updateImeParent() { final SurfaceControl newParent = computeImeParent(); - if (newParent != null) { + if (newParent != null && newParent != mInputMethodSurfaceParent) { + mInputMethodSurfaceParent = newParent; getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent); scheduleAnimation(); } @@ -4426,18 +4459,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // // In the case of split-screen windowing mode, we need to elevate the IME above the // docked divider while keeping the app itself below the docked divider, so instead - // we use relative layering of the IME targets child windows, and place the IME in - // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}). + // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild // // In the case the IME target is animating, the animation Z order may be different // than the WindowContainer Z order, so it's difficult to be sure we have the correct - // IME target. In this case we just layer the IME over all transitions by placing it - // in the above applications layer. + // IME target. In this case we just layer the IME over its parent surface. // - // In the case where we have no IME target we assign it where its base layer would - // place it in the AboveAppWindowContainers. + // In the case where we have no IME target we let its window parent to place it. // - // Keep IME window in mAboveAppWindowsContainers as long as app's starting window + // Keep IME window in surface parent as long as app's starting window // exists so it get's layered above the starting window. if (imeTarget != null && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow()) && ( @@ -4448,6 +4478,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // TODO: We need to use an extra level on the app surface to ensure // this is always above SurfaceView but always below attached window. 1); + } else if (mInputMethodSurfaceParent != null) { + // The IME surface parent may not be its window parent's surface + // (@see #computeImeParent), so set relative layer here instead of letting the window + // parent to assign layer. + mImeWindowsContainers.assignRelativeLayer(t, mInputMethodSurfaceParent, 1); } super.assignChildLayers(t); } @@ -4481,9 +4516,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } - void assignStackOrdering() { + void assignRootTaskOrdering() { forAllTaskDisplayAreas(taskDisplayArea -> { - taskDisplayArea.assignStackOrdering(getPendingTransaction()); + taskDisplayArea.assignRootTaskOrdering(getPendingTransaction()); }); } @@ -5048,7 +5083,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mWmService.requestTraversal(); } - static boolean alwaysCreateStack(int windowingMode, int activityType) { + static boolean alwaysCreateRootTask(int windowingMode, int activityType) { // Always create a stack for fullscreen, freeform, and split-screen-secondary windowing // modes so that we can manage visual ordering and return types correctly. return activityType == ACTIVITY_TYPE_STANDARD @@ -5062,13 +5097,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp static boolean canReuseExistingTask(int windowingMode, int activityType) { // Existing Tasks can be reused if a new stack will be created anyway, or for the Dream - // because there can only ever be one DreamActivity. - return alwaysCreateStack(windowingMode, activityType) + return alwaysCreateRootTask(windowingMode, activityType) || activityType == ACTIVITY_TYPE_DREAM; } @Nullable - Task getFocusedStack() { - return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedStack); + Task getFocusedRootTask() { + return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedRootTask); } /** @@ -5197,7 +5232,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration); super.onRequestedOverrideConfigurationChanged(overrideConfiguration); mCurrentOverrideConfigurationChanges = 0; - mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this); + mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this); mAtmService.addWindowLayoutReasons( ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED); } @@ -5305,19 +5340,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Check if all task display areas have only the empty home stacks left. boolean hasNonEmptyHomeStack = forAllTaskDisplayAreas(taskDisplayArea -> { - if (taskDisplayArea.getStackCount() != 1) { + if (taskDisplayArea.getRootTaskCount() != 1) { return true; } - final Task stack = taskDisplayArea.getStackAt(0); + final Task stack = taskDisplayArea.getRootTaskAt(0); return !stack.isActivityTypeHome() || stack.hasChild(); }); if (!hasNonEmptyHomeStack) { // Release this display if only empty home stack(s) are left. This display will be // released along with the stack(s) removal. forAllTaskDisplayAreas(taskDisplayArea -> { - taskDisplayArea.getStackAt(0).removeIfPossible(); + taskDisplayArea.getRootTaskAt(0).removeIfPossible(); }); - } else if (getTopStack() == null) { + } else if (getTopRootTask() == null) { removeIfPossible(); mRootWindowContainer.mTaskSupervisor .getKeyguardController().onDisplayRemoved(mDisplayId); @@ -5344,7 +5379,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } boolean shouldSleep() { - return (getStackCount() == 0 || !mAllSleepTokens.isEmpty()) + return (getRootTaskCount() == 0 || !mAllSleepTokens.isEmpty()) && (mAtmService.mRunningVoice == null); } @@ -5689,4 +5724,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } return count; } + + MagnificationSpec getMagnificationSpec() { + return mMagnificationSpec; + } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 042dd6db9800..2ddd00101769 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -66,7 +66,6 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -279,7 +278,6 @@ public class DisplayPolicy { private volatile boolean mKeyguardDrawComplete; private volatile boolean mWindowManagerDrawComplete; - private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>(); private WindowState mStatusBar = null; private WindowState mNotificationShade = null; private final int[] mStatusBarHeightForRotation = new int[4]; @@ -864,19 +862,7 @@ public class DisplayPolicy { * @param attrs The window layout parameters to be modified. These values * are modified in-place. */ - public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs, - int callingPid, int callingUid) { - - final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; - if (mScreenDecorWindows.contains(win)) { - if (!isScreenDecor) { - // No longer has the flag set, so remove from the set. - mScreenDecorWindows.remove(win); - } - } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) { - mScreenDecorWindows.add(win); - } - + public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: @@ -966,11 +952,6 @@ public class DisplayPolicy { * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mContext.enforcePermission( - android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid, - "DisplayPolicy"); - } if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) { mContext.enforcePermission( android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid, @@ -1090,10 +1071,6 @@ public class DisplayPolicy { * @param attrs Information about the window to be added. */ void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) { - if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { - mScreenDecorWindows.add(win); - } - switch (attrs.type) { case TYPE_NOTIFICATION_SHADE: mNotificationShade = win; @@ -1275,7 +1252,6 @@ public class DisplayPolicy { if (mLastFocusedWindow == win) { mLastFocusedWindow = null; } - mScreenDecorWindows.remove(win); } private int getStatusBarHeight(DisplayFrames displayFrames) { @@ -1457,14 +1433,12 @@ public class DisplayPolicy { } final int fl = attrs.flags; - final int pfl = attrs.privateFlags; final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0 && (fl & FLAG_LAYOUT_INSET_DECOR) != 0; - final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0; final DisplayFrames displayFrames = isFixedRotationTransforming ? windowToken.getFixedRotationTransformDisplayFrames() : mDisplayContent.mDisplayFrames; - if (layoutInScreenAndInsetDecor && !screenDecor) { + if (layoutInScreenAndInsetDecor) { outDisplayCutout.set( displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout()); } else { @@ -1564,7 +1538,6 @@ public class DisplayPolicy { simulatedWindowFrames, barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame)); } - layoutScreenDecorWindows(displayFrames, simulatedWindowFrames); } /** @@ -1585,7 +1558,6 @@ public class DisplayPolicy { layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */); layoutStatusBar(displayFrames, null /* simulatedContentFrame */); - layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */); } void updateHideNavInputEventReceiver() { @@ -1640,47 +1612,6 @@ public class DisplayPolicy { state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom); } - /** - * Layout the decor windows with {@link #PRIVATE_FLAG_IS_SCREEN_DECOR}. - * - * @param displayFrames The display frames to be layouted. - * @param simulatedFrames Non-null if the caller only needs the result of display frames (see - * {@link WindowState#mSimulatedWindowFrames}). - */ - private void layoutScreenDecorWindows(DisplayFrames displayFrames, - WindowFrames simulatedFrames) { - if (mScreenDecorWindows.isEmpty()) { - return; - } - - sTmpRect.setEmpty(); - final int displayId = displayFrames.mDisplayId; - - for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) { - final WindowState w = mScreenDecorWindows.valueAt(i); - if (w.getDisplayId() != displayId || !w.isVisible()) { - // Skip if not on the same display or not visible. - continue; - } - - final boolean isSimulatedLayout = simulatedFrames != null; - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(simulatedFrames); - } - getRotatedWindowBounds(displayFrames, w, sTmpScreenDecorFrame); - final WindowFrames windowFrames = w.getLayoutingWindowFrames(); - windowFrames.setFrames(sTmpScreenDecorFrame /* parentFrame */, - sTmpScreenDecorFrame /* displayFrame */); - try { - w.computeFrame(displayFrames); - } finally { - if (isSimulatedLayout) { - w.setSimulatedWindowFrames(null); - } - } - } - } - private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) { // decide where the status bar goes ahead of time if (mStatusBar == null) { @@ -1819,8 +1750,7 @@ public class DisplayPolicy { // We've already done the navigation bar, status bar, and all screen decor windows. If the // status bar can receive input, we need to layout it again to accommodate for the IME // window. - if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar - || mScreenDecorWindows.contains(win)) { + if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) { return; } final WindowManager.LayoutParams attrs = win.getAttrs(); @@ -2116,7 +2046,7 @@ public class DisplayPolicy { // requests to hide the status bar. Not sure if there is another way that to be the // case though. if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea() - .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { topAppHidesStatusBar = false; } } @@ -2900,9 +2830,9 @@ public class DisplayPolicy { private int updateSystemBarsLw(WindowState win, int disableFlags) { final boolean dockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean freeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isStackVisible(WINDOWING_MODE_FREEFORM); + .isRootTaskVisible(WINDOWING_MODE_FREEFORM); final boolean resizing = mDisplayContent.getDockedDividerController().isResizing(); // We need to force system bars when the docked stack is visible, when the freeform stack diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 78f1426348a7..f4fdd7370e9c 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,9 +16,9 @@ package com.android.server.wm; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -64,7 +64,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { ? "DisplayWindowSettingsProvider" : TAG_WM; private static final String DATA_DISPLAY_SETTINGS_FILE_PATH = "system/display_settings.xml"; - private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/display_settings.xml"; + private static final String VENDOR_DISPLAY_SETTINGS_FILE_PATH = "etc/display_settings.xml"; private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays"; private static final int IDENTIFIER_UNIQUE_ID = 0; @@ -86,34 +86,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { void finishWrite(OutputStream os, boolean success); } - private final ReadableSettingsStorage mVendorSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the base (vendor) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mVendorIdentifierType; - private final Map<String, SettingsEntry> mVendorSettings = new HashMap<>(); - - private final WritableSettingsStorage mOverrideSettingsStorage; - /** - * The preferred type of a display identifier to use when storing and retrieving entries from - * the data (override) settings file. - * - * @see #getIdentifier(DisplayInfo, int) - */ - @DisplayIdentifierType - private int mOverrideIdentifierType; - private final Map<String, SettingsEntry> mOverrideSettings = new HashMap<>(); - - /** - * Enables or disables settings provided from the vendor settings storage. - * - * @see #setVendorSettingsIgnored(boolean) - */ - private boolean mIgnoreVendorSettings = true; + private ReadableSettings mBaseSettings; + private final WritableSettings mOverrideSettings; DisplayWindowSettingsProvider() { this(new AtomicFileStorage(getVendorSettingsFile()), @@ -121,43 +95,48 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @VisibleForTesting - DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage vendorSettingsStorage, + DisplayWindowSettingsProvider(@NonNull ReadableSettingsStorage baseSettingsStorage, @NonNull WritableSettingsStorage overrideSettingsStorage) { - mVendorSettingsStorage = vendorSettingsStorage; - mOverrideSettingsStorage = overrideSettingsStorage; - readSettings(); + mBaseSettings = new ReadableSettings(baseSettingsStorage); + mOverrideSettings = new WritableSettings(overrideSettingsStorage); } /** - * Enables or disables settings provided from the vendor settings storage. If {@code true}, the - * vendor settings will be ignored and only the override settings will be returned from - * {@link #getSettings(DisplayInfo)}. If {@code false}, settings returned from - * {@link #getSettings(DisplayInfo)} will be a merged result of the vendor settings and the - * override settings. + * Overrides the path for the file that should be used to read base settings. If {@code null} is + * passed the default base settings file path will be used. + * + * @see #VENDOR_DISPLAY_SETTINGS_FILE_PATH */ - void setVendorSettingsIgnored(boolean ignored) { - mIgnoreVendorSettings = ignored; + void setBaseSettingsFilePath(@Nullable String path) { + AtomicFile settingsFile; + if (path != null) { + settingsFile = new AtomicFile(new File(path), WM_DISPLAY_COMMIT_TAG); + } else { + settingsFile = getVendorSettingsFile(); + } + + setBaseSettingsStorage(new AtomicFileStorage(settingsFile)); } /** - * Returns whether or not the vendor settings are being ignored. + * Overrides the storage that should be used to read base settings. * - * @see #setVendorSettingsIgnored(boolean) + * @see #setBaseSettingsFilePath(String) */ @VisibleForTesting - boolean getVendorSettingsIgnored() { - return mIgnoreVendorSettings; + void setBaseSettingsStorage(@NonNull ReadableSettingsStorage baseSettingsStorage) { + mBaseSettings = new ReadableSettings(baseSettingsStorage); } @Override @NonNull public SettingsEntry getSettings(@NonNull DisplayInfo info) { - SettingsEntry vendorSettings = getVendorSettingsEntry(info); - SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - if (vendorSettings == null) { + SettingsEntry baseSettings = mBaseSettings.getSettingsEntry(info); + SettingsEntry overrideSettings = mOverrideSettings.getOrCreateSettingsEntry(info); + if (baseSettings == null) { return new SettingsEntry(overrideSettings); } else { - SettingsEntry mergedSettings = new SettingsEntry(vendorSettings); + SettingsEntry mergedSettings = new SettingsEntry(baseSettings); mergedSettings.updateFrom(overrideSettings); return mergedSettings; } @@ -166,99 +145,126 @@ class DisplayWindowSettingsProvider implements SettingsProvider { @Override @NonNull public SettingsEntry getOverrideSettings(@NonNull DisplayInfo info) { - return new SettingsEntry(getOrCreateOverrideSettingsEntry(info)); + return new SettingsEntry(mOverrideSettings.getOrCreateSettingsEntry(info)); } @Override public void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides) { - final SettingsEntry overrideSettings = getOrCreateOverrideSettingsEntry(info); - boolean changed = overrideSettings.setTo(overrides); - if (changed) { - writeOverrideSettings(); - } + mOverrideSettings.updateSettingsEntry(info, overrides); } - @Nullable - private SettingsEntry getVendorSettingsEntry(DisplayInfo info) { - if (mIgnoreVendorSettings) { + /** + * Class that allows reading {@link SettingsEntry entries} from a + * {@link ReadableSettingsStorage}. + */ + private static class ReadableSettings { + /** + * The preferred type of a display identifier to use when storing and retrieving entries + * from the settings entries. + * + * @see #getIdentifier(DisplayInfo) + */ + @DisplayIdentifierType + protected int mIdentifierType; + protected final Map<String, SettingsEntry> mSettings = new HashMap<>(); + + ReadableSettings(ReadableSettingsStorage settingsStorage) { + loadSettings(settingsStorage); + } + + @Nullable + final SettingsEntry getSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + return settings; + } return null; } - final String identifier = getIdentifier(info, mVendorIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mVendorSettings.get(identifier)) != null) { - return settings; + /** Gets the identifier of choice for the current config. */ + protected final String getIdentifier(DisplayInfo displayInfo) { + if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) { + // Config suggests using port as identifier for physical displays. + if (displayInfo.address instanceof DisplayAddress.Physical) { + return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); + } + } + return displayInfo.uniqueId; } - // Else, fall back to the display name. - if ((settings = mVendorSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mVendorSettings.remove(info.name); - mVendorSettings.put(identifier, settings); - return settings; + + private void loadSettings(ReadableSettingsStorage settingsStorage) { + FileData fileData = readSettings(settingsStorage); + if (fileData != null) { + mIdentifierType = fileData.mIdentifierType; + mSettings.putAll(fileData.mSettings); + } } - return null; } - @NonNull - private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) { - final String identifier = getIdentifier(info, mOverrideIdentifierType); - SettingsEntry settings; - // Try to get corresponding settings using preferred identifier for the current config. - if ((settings = mOverrideSettings.get(identifier)) != null) { - return settings; - } - // Else, fall back to the display name. - if ((settings = mOverrideSettings.get(info.name)) != null) { - // Found an entry stored with old identifier. - mOverrideSettings.remove(info.name); - mOverrideSettings.put(identifier, settings); - writeOverrideSettings(); - return settings; + /** + * Class that allows reading {@link SettingsEntry entries} from, and writing entries to, a + * {@link WritableSettingsStorage}. + */ + private static final class WritableSettings extends ReadableSettings { + private final WritableSettingsStorage mSettingsStorage; + + WritableSettings(WritableSettingsStorage settingsStorage) { + super(settingsStorage); + mSettingsStorage = settingsStorage; } - settings = new SettingsEntry(); - mOverrideSettings.put(identifier, settings); - return settings; - } + @NonNull + SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) { + final String identifier = getIdentifier(info); + SettingsEntry settings; + // Try to get corresponding settings using preferred identifier for the current config. + if ((settings = mSettings.get(identifier)) != null) { + return settings; + } + // Else, fall back to the display name. + if ((settings = mSettings.get(info.name)) != null) { + // Found an entry stored with old identifier. + mSettings.remove(info.name); + mSettings.put(identifier, settings); + writeSettings(); + return settings; + } - private void readSettings() { - FileData vendorFileData = readSettings(mVendorSettingsStorage); - if (vendorFileData != null) { - mVendorIdentifierType = vendorFileData.mIdentifierType; - mVendorSettings.putAll(vendorFileData.mSettings); + settings = new SettingsEntry(); + mSettings.put(identifier, settings); + return settings; } - FileData overrideFileData = readSettings(mOverrideSettingsStorage); - if (overrideFileData != null) { - mOverrideIdentifierType = overrideFileData.mIdentifierType; - mOverrideSettings.putAll(overrideFileData.mSettings); + void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) { + final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info); + final boolean changed = overrideSettings.setTo(settings); + if (changed) { + writeSettings(); + } } - } - private void writeOverrideSettings() { - FileData fileData = new FileData(); - fileData.mIdentifierType = mOverrideIdentifierType; - fileData.mSettings.putAll(mOverrideSettings); - writeSettings(mOverrideSettingsStorage, fileData); - } - - /** Gets the identifier of choice for the current config. */ - private static String getIdentifier(DisplayInfo displayInfo, @DisplayIdentifierType int type) { - if (type == IDENTIFIER_PORT && displayInfo.address != null) { - // Config suggests using port as identifier for physical displays. - if (displayInfo.address instanceof DisplayAddress.Physical) { - return "port:" + ((DisplayAddress.Physical) displayInfo.address).getPort(); - } + private void writeSettings() { + FileData fileData = new FileData(); + fileData.mIdentifierType = mIdentifierType; + fileData.mSettings.putAll(mSettings); + DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData); } - return displayInfo.uniqueId; } @NonNull private static AtomicFile getVendorSettingsFile() { final File vendorFile = new File(Environment.getVendorDirectory(), - VENDOR_DISPLAY_SETTINGS_PATH); + VENDOR_DISPLAY_SETTINGS_FILE_PATH); return new AtomicFile(vendorFile, WM_DISPLAY_COMMIT_TAG); } @@ -335,36 +341,31 @@ class DisplayWindowSettingsProvider implements SettingsProvider { return fileData; } - private static int getIntAttribute(XmlPullParser parser, String name, int defaultValue) { - try { - final String str = parser.getAttributeValue(null, name); - return str != null ? Integer.parseInt(str) : defaultValue; - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e); - return defaultValue; - } + private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) { + return parser.getAttributeInt(null, name, defaultValue); } @Nullable - private static Integer getIntegerAttribute(XmlPullParser parser, String name, + private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name, @Nullable Integer defaultValue) { try { - final String str = parser.getAttributeValue(null, name); - return str != null ? Integer.valueOf(str) : defaultValue; - } catch (NumberFormatException e) { - Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e); + return parser.getAttributeInt(null, name); + } catch (Exception ignored) { return defaultValue; } } @Nullable - private static Boolean getBooleanAttribute(XmlPullParser parser, String name, + private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name, @Nullable Boolean defaultValue) { - final String str = parser.getAttributeValue(null, name); - return str != null ? Boolean.valueOf(str) : defaultValue; + try { + return parser.getAttributeBoolean(null, name); + } catch (Exception ignored) { + return defaultValue; + } } - private static void readDisplay(XmlPullParser parser, FileData fileData) + private static void readDisplay(TypedXmlPullParser parser, FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); if (name != null) { @@ -407,7 +408,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { XmlUtils.skipCurrentTag(parser); } - private static void readConfig(XmlPullParser parser, FileData fileData) + private static void readConfig(TypedXmlPullParser parser, FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { fileData.mIdentifierType = getIntAttribute(parser, "identifier", @@ -432,8 +433,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.startTag(null, "display-settings"); out.startTag(null, "config"); - out.attribute(null, "identifier", - Integer.toString(data.mIdentifierType)); + out.attributeInt(null, "identifier", data.mIdentifierType); out.endTag(null, "config"); for (Map.Entry<String, SettingsEntry> entry @@ -447,8 +447,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { out.startTag(null, "display"); out.attribute(null, "name", displayIdentifier); if (settingsEntry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) { - out.attribute(null, "windowingMode", - Integer.toString(settingsEntry.mWindowingMode)); + out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode); } if (settingsEntry.mUserRotationMode != null) { out.attribute(null, "userRotationMode", @@ -459,22 +458,18 @@ class DisplayWindowSettingsProvider implements SettingsProvider { settingsEntry.mUserRotation.toString()); } if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) { - out.attribute(null, "forcedWidth", - Integer.toString(settingsEntry.mForcedWidth)); - out.attribute(null, "forcedHeight", - Integer.toString(settingsEntry.mForcedHeight)); + out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth); + out.attributeInt(null, "forcedHeight", settingsEntry.mForcedHeight); } if (settingsEntry.mForcedDensity != 0) { - out.attribute(null, "forcedDensity", - Integer.toString(settingsEntry.mForcedDensity)); + out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity); } if (settingsEntry.mForcedScalingMode != null) { out.attribute(null, "forcedScalingMode", settingsEntry.mForcedScalingMode.toString()); } if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) { - out.attribute(null, "removeContentMode", - Integer.toString(settingsEntry.mRemoveContentMode)); + out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode); } if (settingsEntry.mShouldShowWithInsecureKeyguard != null) { out.attribute(null, "shouldShowWithInsecureKeyguard", diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java index cdc14cd11228..92baadf5ee69 100644 --- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java +++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java @@ -27,7 +27,7 @@ import android.util.ArraySet; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.server.wm.utils.DeviceConfigInterface; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java index 4793e1b6fb9f..b0afc57b647b 100644 --- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java +++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java @@ -18,6 +18,9 @@ package com.android.server.wm; import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -29,7 +32,9 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Resources; +import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.RectF; import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.Bundle; @@ -43,6 +48,7 @@ import android.service.attestation.IImpressionAttestationService; import android.service.attestation.ImpressionAttestationService; import android.service.attestation.ImpressionToken; import android.util.Slog; +import android.view.MagnificationSpec; import com.android.internal.annotations.GuardedBy; @@ -59,7 +65,8 @@ import java.util.function.BiConsumer; * blocking calls into another service. */ public class ImpressionAttestationController { - private static final String TAG = "ImpressionAttestationController"; + private static final String TAG = + TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM; private static final boolean DEBUG = false; private final Object mServiceConnectionLock = new Object(); @@ -81,6 +88,10 @@ public class ImpressionAttestationController { private final String mSalt; + private final float[] mTmpFloat9 = new float[9]; + private final Matrix mTmpMatrix = new Matrix(); + private final RectF mTmpRectF = new RectF(); + private interface Command { void run(IImpressionAttestationService service) throws RemoteException; } @@ -151,6 +162,79 @@ public class ImpressionAttestationController { } /** + * Calculate the bounds to take the screenshot when generating the impression token. This takes + * into account window transform, magnification, and display bounds. + * + * Call while holding {@link WindowManagerService#mGlobalLock} + * + * @param win Window that the impression token is generated for. + * @param boundsInWindow The bounds passed in about where in the window to take the screenshot. + * @param outBounds The result of the calculated bounds + */ + void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow, + Rect outBounds) { + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow); + } + outBounds.set(boundsInWindow); + + DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + return; + } + + // Intersect boundsInWindow with the window to make sure it's not outside the window + // requesting the token. Offset the window bounds to 0,0 since the boundsInWindow are + // offset from the window location, not display. + final Rect windowBounds = new Rect(); + win.getBounds(windowBounds); + windowBounds.offsetTo(0, 0); + outBounds.intersectUnchecked(windowBounds); + + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds); + } + + if (outBounds.isEmpty()) { + return; + } + + // Transform the bounds using the window transform in case there's a scale or offset. + // This allows the bounds to be in display space. + win.getTransformationMatrix(mTmpFloat9, mTmpMatrix); + mTmpRectF.set(outBounds); + mTmpMatrix.mapRect(mTmpRectF, mTmpRectF); + outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right, + (int) mTmpRectF.bottom); + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds); + } + + // Apply the magnification spec values to the bounds since the content could be magnified + final MagnificationSpec magSpec = displayContent.getMagnificationSpec(); + if (magSpec != null) { + outBounds.scale(magSpec.scale); + outBounds.offset((int) magSpec.offsetX, (int) magSpec.offsetY); + } + + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds); + } + + if (outBounds.isEmpty()) { + return; + } + + // Intersect with the display bounds since it shouldn't take a screenshot of content + // outside the display since it's not visible to the user. + final Rect displayBounds = displayContent.getBounds(); + outBounds.intersectUnchecked(displayBounds); + if (DEBUG) { + Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds); + } + } + + /** * Run a command, starting the service connection if necessary. */ private void connectAndRun(@NonNull Command command) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index b49d83d93d54..25d779f0fb33 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -40,6 +40,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT; @@ -655,6 +656,7 @@ final class InputMonitor { || type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_DOCK_DIVIDER || type == TYPE_ACCESSIBILITY_OVERLAY - || type == TYPE_INPUT_CONSUMER; + || type == TYPE_INPUT_CONSUMER + || type == TYPE_VOICE_INTERACTION; } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index d308766b9fe5..ee150c31184c 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -392,9 +392,9 @@ class InsetsPolicy { private boolean forceShowsSystemBarsForWindowingMode() { final boolean isDockedStackVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + .isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); final boolean isFreeformStackVisible = mDisplayContent.getDefaultTaskDisplayArea() - .isStackVisible(WINDOWING_MODE_FREEFORM); + .isRootTaskVisible(WINDOWING_MODE_FREEFORM); final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing(); // We need to force system bars when the docked stack is visible, when the freeform stack diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 9d70fd771f31..752d6b4fc908 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -496,10 +496,7 @@ class InsetsStateController { } void notifyInsetsChanged() { - mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */); - if (mDisplayContent.mRemoteInsetsControlTarget != null) { - mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged(); - } + mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged); } void dump(String prefix, PrintWriter pw) { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index ebd91a093326..e45310a99fbd 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -211,7 +211,7 @@ class KeyguardController { updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); mRootWindowContainer.addStartingWindowsForVisibleActivities(); mWindowManager.executeAppTransition(); @@ -595,8 +595,8 @@ class KeyguardController { @Nullable private Task getRootTaskForControllingOccluding(DisplayContent display) { return display.getItemFromTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task task = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task task = taskDisplayArea.getRootTaskAt(sNdx); if (task != null && task.isFocusableAndVisible() && !task.inPinnedWindowingMode()) { return task; diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java index b6b172eeae5b..8745e95dba4d 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsController.java +++ b/services/core/java/com/android/server/wm/LaunchParamsController.java @@ -143,7 +143,7 @@ class LaunchParamsController { try { if (mTmpParams.mPreferredTaskDisplayArea != null && task.getDisplayArea() != mTmpParams.mPreferredTaskDisplayArea) { - mService.mRootWindowContainer.moveStackToTaskDisplayArea(task.getRootTaskId(), + mService.mRootWindowContainer.moveRootTaskToTaskDisplayArea(task.getRootTaskId(), mTmpParams.mPreferredTaskDisplayArea, true /* onTop */); } diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java index 2f8cfdd7002c..391e6598dca2 100644 --- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java +++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java @@ -27,26 +27,24 @@ import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.DisplayInfo; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.server.LocalServices; import com.android.server.pm.PackageList; import com.android.server.wm.LaunchParamsController.LaunchParams; -import libcore.io.IoUtils; - import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; -import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; -import java.io.StringWriter; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -195,12 +193,9 @@ class LaunchParamsPersister { continue; } - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader(paramsFile)); + try (InputStream in = new FileInputStream(paramsFile)) { final PersistableLaunchParams params = new PersistableLaunchParams(); - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(reader); + final TypedXmlPullParser parser = Xml.resolvePullParser(in); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT && event != XmlPullParser.END_TAG) { @@ -223,8 +218,6 @@ class LaunchParamsPersister { } catch (Exception e) { Slog.w(TAG, "Failed to restore launch params for " + name, e); filesToDelete.add(paramsFile); - } finally { - IoUtils.closeQuietly(reader); } } @@ -410,12 +403,11 @@ class LaunchParamsPersister { mLaunchParams = launchParams; } - private StringWriter saveParamsToXml() { - final StringWriter writer = new StringWriter(); - final XmlSerializer serializer = new FastXmlSerializer(); - + private byte[] saveParamsToXml() { try { - serializer.setOutput(writer); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final TypedXmlSerializer serializer = Xml.resolveSerializer(os); + serializer.startDocument(/* encoding */ null, /* standalone */ true); serializer.startTag(null, TAG_LAUNCH_PARAMS); @@ -425,7 +417,7 @@ class LaunchParamsPersister { serializer.endDocument(); serializer.flush(); - return writer; + return os.toByteArray(); } catch (IOException e) { return null; } @@ -433,7 +425,7 @@ class LaunchParamsPersister { @Override public void process() { - final StringWriter writer = saveParamsToXml(); + final byte[] data = saveParamsToXml(); final File launchParamFolder = getLaunchParamFolder(mUserId); if (!launchParamFolder.isDirectory() && !launchParamFolder.mkdirs()) { @@ -447,7 +439,7 @@ class LaunchParamsPersister { FileOutputStream stream = null; try { stream = atomicFile.startWrite(); - stream.write(writer.toString().getBytes()); + stream.write(data); } catch (Exception e) { Slog.e(TAG, "Failed to write param file for " + mComponentName, e); if (stream != null) { @@ -513,17 +505,16 @@ class LaunchParamsPersister { */ long mTimestamp; - void saveToXml(XmlSerializer serializer) throws IOException { + void saveToXml(TypedXmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId); - serializer.attribute(null, ATTR_WINDOWING_MODE, - Integer.toString(mWindowingMode)); + serializer.attributeInt(null, ATTR_WINDOWING_MODE, mWindowingMode); serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString()); if (mWindowLayoutAffinity != null) { serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); } } - void restore(File xmlFile, XmlPullParser parser) { + void restore(File xmlFile, TypedXmlPullParser parser) { for (int i = 0; i < parser.getAttributeCount(); ++i) { final String attrValue = parser.getAttributeValue(i); switch (parser.getAttributeName(i)) { diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index ee3978746488..4b3a43432fc5 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -504,7 +504,7 @@ public class LockTaskController { return; } task.performClearTaskLocked(); - mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities(); + mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities(); } /** @@ -640,7 +640,7 @@ public class LockTaskController { if (andResume) { mSupervisor.findTaskToMoveToFront(task, 0, null, reason, lockTaskModeState != LOCK_TASK_MODE_NONE); - mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities(); + mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities(); final Task rootTask = task.getRootTask(); if (rootTask != null) { rootTask.mDisplayContent.executeAppTransition(); @@ -717,7 +717,7 @@ public class LockTaskController { } if (taskChanged) { - mSupervisor.mRootWindowContainer.resumeFocusedStacksTopActivities(); + mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities(); } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index ba6b27ac3252..5598937da63d 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -36,13 +36,13 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.Process.SYSTEM_UID; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TASKS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskSupervisor.REMOVE_FROM_RECENTS; import android.annotation.Nullable; import android.app.ActivityManager; @@ -220,7 +220,7 @@ class RecentTasks { final RootWindowContainer rac = mService.mRootWindowContainer; final DisplayContent dc = rac.getDisplayContent(displayId).mDisplayContent; if (dc.pointWithinAppWindow(x, y)) { - final Task stack = mService.getTopDisplayFocusedStack(); + final Task stack = mService.getTopDisplayFocusedRootTask(); final Task topTask = stack != null ? stack.getTopMostTask() : null; resetFreezeTaskListReordering(topTask); } @@ -328,7 +328,7 @@ class RecentTasks { @VisibleForTesting void resetFreezeTaskListReorderingOnTimeout() { synchronized (mService.mGlobalLock) { - final Task focusedStack = mService.getTopDisplayFocusedStack(); + final Task focusedStack = mService.getTopDisplayFocusedRootTask(); final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null; resetFreezeTaskListReordering(topTask); } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 823dc51e939f..067c772dad93 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -29,7 +29,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; -import static com.android.server.wm.TaskDisplayArea.getStackAbove; +import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; import android.annotation.Nullable; import android.app.ActivityOptions; @@ -45,13 +45,13 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; import com.android.server.wm.ActivityMetricsLogger.LaunchingState; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; +import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener; /** * Manages the recents animation, including the reordering of the stacks for the transition and * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. */ -class RecentsAnimation implements RecentsAnimationCallbacks, - TaskDisplayArea.OnStackOrderChangedListener { +class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChangedListener { private static final String TAG = RecentsAnimation.class.getSimpleName(); private final ActivityTaskManagerService mService; @@ -106,7 +106,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, void preloadRecentsActivity() { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s", mTargetIntent); - Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + Task targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, mTargetActivityType); ActivityRecord targetActivity = getTargetActivity(targetStack); if (targetActivity != null) { @@ -127,7 +127,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Create the activity record. Because the activity is invisible, this doesn't really // start the client. startRecentsActivityInBackground("preloadRecents"); - targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, mTargetActivityType); targetActivity = getTargetActivity(targetStack); if (targetActivity == null) { @@ -165,12 +165,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); // If the activity is associated with the recents stack, then try and get that first - Task targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + Task targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, mTargetActivityType); ActivityRecord targetActivity = getTargetActivity(targetStack); final boolean hasExistingActivity = targetActivity != null; if (hasExistingActivity) { - mRestoreTargetBehindStack = getStackAbove(targetStack); + mRestoreTargetBehindStack = getRootTaskAbove(targetStack); if (mRestoreTargetBehindStack == null) { notifyAnimationCancelBeforeStart(recentsAnimationRunner); ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, @@ -197,9 +197,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks, try { if (hasExistingActivity) { // Move the recents activity into place for the animation if it is not top most - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetStack); ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s", - targetStack, getStackAbove(targetStack)); + targetStack, getRootTaskAbove(targetStack)); // If there are multiple tasks in the target stack (ie. the home stack, with 3p // and default launchers coexisting), then move the task to the top as a part of @@ -213,12 +213,12 @@ class RecentsAnimation implements RecentsAnimationCallbacks, startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); // Move the recents activity into place for the animation - targetStack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + targetStack = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, mTargetActivityType); targetActivity = getTargetActivity(targetStack); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(targetStack); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetStack); ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved stack=%s behind stack=%s", - targetStack, getStackAbove(targetStack)); + targetStack, getRootTaskAbove(targetStack)); mWindowManager.prepareAppTransitionNone(); mWindowManager.executeAppTransition(); @@ -257,7 +257,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, START_TASK_TO_FRONT, targetActivity, options); // Register for stack order changes - mDefaultTaskDisplayArea.registerStackOrderChangedListener(this); + mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this); } catch (Exception e) { Slog.e(TAG, "Failed to start recents activity", e); throw e; @@ -275,7 +275,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.getRecentsAnimationController(), reorderMode); // Unregister for stack order changes - mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(this); + mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(this); final RecentsAnimationController controller = mWindowManager.getRecentsAnimationController(); @@ -303,7 +303,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, try { mWindowManager.cleanupRecentsAnimation(reorderMode); - final Task targetStack = mDefaultTaskDisplayArea.getStack( + final Task targetStack = mDefaultTaskDisplayArea.getRootTask( WINDOWING_MODE_UNDEFINED, mTargetActivityType); // Prefer to use the original target activity instead of top activity because // we may have moved another task to top (starting 3p launcher). @@ -348,10 +348,10 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){ // Restore the target stack to its previous position final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea(); - taskDisplayArea.moveStackBehindStack(targetStack, + taskDisplayArea.moveRootTaskBehindRootTask(targetStack, mRestoreTargetBehindStack); if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) { - final Task aboveTargetStack = getStackAbove(targetStack); + final Task aboveTargetStack = getRootTaskAbove(targetStack); if (mRestoreTargetBehindStack != null && aboveTargetStack != mRestoreTargetBehindStack) { ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS, @@ -378,7 +378,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, mWindowManager.prepareAppTransitionNone(); mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false); - mService.mRootWindowContainer.resumeFocusedStacksTopActivities(); + mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); // No reason to wait for the pausing activity in this case, as the hiding of // surfaces needs to be done immediately. @@ -412,9 +412,9 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } @Override - public void onStackOrderChanged(Task stack) { - ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", stack); - if (mDefaultTaskDisplayArea.getIndexOf(stack) == -1 || !stack.shouldBeVisible(null)) { + public void onRootTaskOrderChanged(Task rootTask) { + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onStackOrderChanged(): stack=%s", rootTask); + if (mDefaultTaskDisplayArea.getIndexOf(rootTask) == -1 || !rootTask.shouldBeVisible(null)) { // The stack is not visible, so ignore this change return; } @@ -428,8 +428,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // cases: // 1) The next launching task is not being animated by the recents animation // 2) The next task is home activity. (i.e. pressing home key to back home in recents). - if ((!controller.isAnimatingTask(stack.getTopMostTask()) - || controller.isTargetApp(stack.getTopNonFinishingActivity())) + if ((!controller.isAnimatingTask(rootTask.getTopMostTask()) + || controller.isTargetApp(rootTask.getTopNonFinishingActivity())) && controller.shouldDeferCancelUntilNextTransition()) { // Always prepare an app transition since we rely on the transition callbacks to cleanup mWindowManager.prepareAppTransitionNone(); @@ -468,8 +468,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks, * @return The top stack that is not always-on-top. */ private Task getTopNonAlwaysOnTopStack() { - for (int i = mDefaultTaskDisplayArea.getStackCount() - 1; i >= 0; i--) { - final Task s = mDefaultTaskDisplayArea.getStackAt(i); + for (int i = mDefaultTaskDisplayArea.getRootTaskCount() - 1; i >= 0; i--) { + final Task s = mDefaultTaskDisplayArea.getRootTaskAt(i); if (s.getWindowConfiguration().isAlwaysOnTop()) { continue; } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index abee032d042a..5da668c8c361 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -398,7 +398,7 @@ public class RecentsAnimationController implements DeathRecipient { final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea() .getVisibleTasks(); final Task targetStack = mDisplayContent.getDefaultTaskDisplayArea() - .getStack(WINDOWING_MODE_UNDEFINED, targetActivityType); + .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType); if (targetStack != null) { final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) -> { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class), diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java index 7bd5d03f1bc1..17cb8905f260 100644 --- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java +++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java @@ -237,7 +237,7 @@ class ResetTargetTaskHelper { while (!mPendingReparentActivities.isEmpty()) { final ActivityRecord r = mPendingReparentActivities.remove(0); - final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode, + final boolean alwaysCreateTask = DisplayContent.alwaysCreateRootTask(windowingMode, activityType); final Task task = alwaysCreateTask ? taskDisplayArea.getBottomMostTask() : mTargetStack.getBottomMostTask(); @@ -251,7 +251,7 @@ class ResetTargetTaskHelper { } if (targetTask == null) { if (alwaysCreateTask) { - targetTask = taskDisplayArea.getOrCreateStack(windowingMode, + targetTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, false /* onTop */); } else { targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/, diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 707354823817..497087a967f3 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -256,8 +256,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> /** The current user */ int mCurrentUser; - /** Stack id of the front stack when user switched, indexed by userId. */ - SparseIntArray mUserStackInFront = new SparseIntArray(2); + /** Root task id of the front root task when user switched, indexed by userId. */ + SparseIntArray mUserRootTaskInFront = new SparseIntArray(2); /** * A list of tokens that cause the top activity to be put to sleep. @@ -296,7 +296,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> c.recycle(); } finally { mTaskSupervisor.endDeferResume(); - resumeFocusedStacksTopActivities(); + resumeFocusedTasksTopActivities(); } } } @@ -1484,7 +1484,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean fromHomeKey) { // Fallback to top focused display or default display if the displayId is invalid. if (displayId == INVALID_DISPLAY) { - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); displayId = stack != null ? stack.getDisplayId() : DEFAULT_DISPLAY; } @@ -1510,7 +1510,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean allowInstrumenting, boolean fromHomeKey) { // Fallback to top focused display area if the provided one is invalid. if (taskDisplayArea == null) { - final Task stack = getTopDisplayFocusedStack(); + final Task stack = getTopDisplayFocusedRootTask(); taskDisplayArea = stack != null ? stack.getDisplayArea() : getDefaultTaskDisplayArea(); } @@ -1677,7 +1677,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Only resume home activity if isn't finishing. if (r != null && !r.finishing) { r.moveFocusableActivityToTop(myReason); - return resumeFocusedStacksTopActivities(r.getRootTask(), prev, null); + return resumeFocusedTasksTopActivities(r.getRootTask(), prev, null); } return startHomeOnTaskDisplayArea(mCurrentUser, myReason, taskDisplayArea, false /* allowInstrumenting */, false /* fromHomeKey */); @@ -1804,10 +1804,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Configuration config = null; if (displayContent != null) { config = displayContent.updateOrientation( - getDisplayOverrideConfiguration(displayId), - starting != null && starting.mayFreezeScreenLocked() - ? starting.appToken : null, - true /* forceUpdate */); + getDisplayOverrideConfiguration(displayId), starting, true /* forceUpdate */); } // Visibilities may change so let the starting activity have a chance to report. Can't do it // when visibility is changed in each AppWindowToken because it may trigger wrong @@ -1834,12 +1831,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> */ List<IBinder> getTopVisibleActivities() { final ArrayList<IBinder> topActivityTokens = new ArrayList<>(); - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); // Traverse all displays. forAllTaskDisplayAreas(taskDisplayArea -> { // Traverse all stacks on a display area. - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); // Get top activity from a visible stack and add it to the list. if (stack.shouldBeVisible(null /* starting */)) { final ActivityRecord top = stack.getTopNonFinishingActivity(); @@ -1857,9 +1854,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } @Nullable - Task getTopDisplayFocusedStack() { + Task getTopDisplayFocusedRootTask() { for (int i = getChildCount() - 1; i >= 0; --i) { - final Task focusedStack = getChildAt(i).getFocusedStack(); + final Task focusedStack = getChildAt(i).getFocusedRootTask(); if (focusedStack != null) { return focusedStack; } @@ -1869,7 +1866,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> @Nullable ActivityRecord getTopResumedActivity() { - final Task focusedStack = getTopDisplayFocusedStack(); + final Task focusedStack = getTopDisplayFocusedRootTask(); if (focusedStack == null) { return null; } @@ -1882,8 +1879,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity); } - boolean isTopDisplayFocusedStack(Task stack) { - return stack != null && stack == getTopDisplayFocusedStack(); + boolean isTopDisplayFocusedRootTask(Task task) { + return task != null && task == getTopDisplayFocusedRootTask(); } void updatePreviousProcess(ActivityRecord r) { @@ -1894,9 +1891,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // previous app if this activity is being hosted by the process that is actually still the // foreground. WindowProcessController fgApp = reduceOnAllTaskDisplayAreas((taskDisplayArea, app) -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); - if (isTopDisplayFocusedStack(stack)) { + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); + if (isTopDisplayFocusedRootTask(stack)) { final ActivityRecord resumedActivity = stack.getResumedActivity(); if (resumedActivity != null) { app = resumedActivity.app; @@ -1929,8 +1926,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return; } - for (int taskNdx = displayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) { - final Task rootTask = displayArea.getStackAt(taskNdx); + for (int taskNdx = displayArea.getRootTaskCount() - 1; taskNdx >= 0; --taskNdx) { + final Task rootTask = displayArea.getRootTaskAt(taskNdx); if (rootTask.getVisibility(null /*starting*/) == TASK_VISIBILITY_INVISIBLE) { break; } @@ -2012,7 +2009,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } boolean switchUser(int userId, UserState uss) { - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); final int focusStackId = topFocusedStack != null ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID; // We dismiss the docked stack whenever we switch users. @@ -2024,19 +2021,19 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // appropriate. removeRootTasksInWindowingModes(WINDOWING_MODE_PINNED); - mUserStackInFront.put(mCurrentUser, focusStackId); + mUserRootTaskInFront.put(mCurrentUser, focusStackId); mCurrentUser = userId; mTaskSupervisor.mStartingUsers.add(uss); forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); stack.switchUser(userId); } }); - final int restoreStackId = mUserStackInFront.get(userId); - Task stack = getStack(restoreStackId); + final int restoreStackId = mUserRootTaskInFront.get(userId); + Task stack = getRootTask(restoreStackId); if (stack == null) { stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); } @@ -2051,20 +2048,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } void removeUser(int userId) { - mUserStackInFront.delete(userId); + mUserRootTaskInFront.delete(userId); } /** - * Update the last used stack id for non-current user (current user's last - * used stack is the focused stack) + * Update the last used root task id for non-current user (current user's last + * used root task is the focused root task) */ - void updateUserStack(int userId, Task stack) { + void updateUserRootTask(int userId, Task rootTask) { if (userId != mCurrentUser) { - if (stack == null) { - stack = getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); + if (rootTask == null) { + rootTask = getDefaultTaskDisplayArea().getOrCreateRootHomeTask(); } - mUserStackInFront.put(userId, stack.getRootTaskId()); + mUserRootTaskInFront.put(userId, rootTask.getRootTaskId()); } } @@ -2075,8 +2072,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param taskDisplayArea The task display area to move stack to. * @param onTop Indicates whether container should be place on top or on bottom. */ - void moveStackToTaskDisplayArea(int stackId, TaskDisplayArea taskDisplayArea, boolean onTop) { - final Task stack = getStack(stackId); + void moveRootTaskToTaskDisplayArea(int stackId, TaskDisplayArea taskDisplayArea, + boolean onTop) { + final Task stack = getRootTask(stackId); if (stack == null) { throw new IllegalArgumentException("moveStackToTaskDisplayArea: Unknown stackId=" + stackId); @@ -2104,22 +2102,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> /** * Move stack with all its existing content to specified display. * - * @param stackId Id of stack to move. + * @param rootTaskId Id of stack to move. * @param displayId Id of display to move stack to. * @param onTop Indicates whether container should be place on top or on bottom. */ - void moveStackToDisplay(int stackId, int displayId, boolean onTop) { + void moveRootTaskToDisplay(int rootTaskId, int displayId, boolean onTop) { final DisplayContent displayContent = getDisplayContentOrCreate(displayId); if (displayContent == null) { throw new IllegalArgumentException("moveStackToDisplay: Unknown displayId=" + displayId); } - moveStackToTaskDisplayArea(stackId, displayContent.getDefaultTaskDisplayArea(), onTop); + moveRootTaskToTaskDisplayArea(rootTaskId, displayContent.getDefaultTaskDisplayArea(), + onTop); } - boolean moveTopStackActivityToPinnedRootTask(int rootTaskId) { - final Task rootTask = getStack(rootTaskId); + boolean moveTopRootTaskActivityToPinnedRootTask(int rootTaskId) { + final Task rootTask = getRootTask(rootTaskId); if (rootTask == null) { throw new IllegalArgumentException( "moveTopStackActivityToPinnedRootTask: Unknown rootTaskId=" + rootTaskId); @@ -2138,11 +2137,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } - moveActivityToPinnedStack(r, "moveTopStackActivityToPinnedRootTask"); + moveActivityToPinnedRootTask(r, "moveTopStackActivityToPinnedRootTask"); return true; } - void moveActivityToPinnedStack(ActivityRecord r, String reason) { + void moveActivityToPinnedRootTask(ActivityRecord r, String reason) { mService.deferWindowLayout(); final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); @@ -2162,25 +2161,26 @@ class RootWindowContainer extends WindowContainer<DisplayContent> r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); final boolean singleActivity = task.getChildCount() == 1; - final Task stack; + final Task rootTask; if (singleActivity) { - stack = task; + rootTask = task; } else { // In the case of multiple activities, we will create a new task for it and then // move the PIP activity into the task. - stack = taskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, r.getActivityType(), - ON_TOP, r.info, r.intent, false /* createdByOrganizer */); + rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_UNDEFINED, + r.getActivityType(), ON_TOP, r.info, r.intent, + false /* createdByOrganizer */); // It's possible the task entering PIP is in freeform, so save the last // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore // to its previous freeform bounds. - stack.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds); - stack.setBounds(task.getBounds()); + rootTask.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds); + rootTask.setBounds(task.getBounds()); // There are multiple activities in the task and moving the top activity should // reveal/leave the other activities in their original task. // On the other hand, ActivityRecord#onParentChanged takes care of setting the // up-to-dated pinned stack information on this newly created stack. - r.reparent(stack, MAX_VALUE, reason); + r.reparent(rootTask, MAX_VALUE, reason); // In the case of this activity entering PIP due to it being moved to the back, // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be @@ -2199,17 +2199,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the // ActivityRecord to be fullscreen. - final int intermediateWindowingMode = stack.getWindowingMode(); - if (stack.getParent() != taskDisplayArea) { + final int intermediateWindowingMode = rootTask.getWindowingMode(); + if (rootTask.getParent() != taskDisplayArea) { // stack is nested, but pinned tasks need to be direct children of their // display area, so reparent. - stack.reparent(taskDisplayArea, true /* onTop */); + rootTask.reparent(taskDisplayArea, true /* onTop */); } // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term. r.setWindowingMode(intermediateWindowingMode); - stack.setWindowingMode(WINDOWING_MODE_PINNED); + rootTask.setWindowingMode(WINDOWING_MODE_PINNED); // Reset the state that indicates it can enter PiP while pausing after we've moved it // to the pinned stack @@ -2219,7 +2219,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } ensureActivitiesVisible(null, 0, false /* preserveWindows */); - resumeFocusedStacksTopActivities(); + resumeFocusedTasksTopActivities(); notifyActivityPipModeChanged(r); } @@ -2291,13 +2291,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @return The task id that was finished in this stack, or INVALID_TASK_ID if none was finished. */ int finishTopCrashedActivities(WindowProcessController app, String reason) { - Task focusedStack = getTopDisplayFocusedStack(); + Task focusedStack = getTopDisplayFocusedRootTask(); Task finishedTask = reduceOnAllTaskDisplayAreas((taskDisplayArea, task) -> { // It is possible that request to finish activity might also remove its task and // stack, so we need to be careful with indexes in the loop and check child count // every time. - for (int stackNdx = 0; stackNdx < taskDisplayArea.getStackCount(); ++stackNdx) { - final Task stack = taskDisplayArea.getStackAt(stackNdx); + for (int stackNdx = 0; stackNdx < taskDisplayArea.getRootTaskCount(); ++stackNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(stackNdx); final Task t = stack.finishTopCrashedActivityLocked(app, reason); if (stack == focusedStack || task == null) { task = t; @@ -2308,21 +2308,21 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return finishedTask != null ? finishedTask.mTaskId : INVALID_TASK_ID; } - boolean resumeFocusedStacksTopActivities() { - return resumeFocusedStacksTopActivities(null, null, null); + boolean resumeFocusedTasksTopActivities() { + return resumeFocusedTasksTopActivities(null, null, null); } - boolean resumeFocusedStacksTopActivities( - Task targetStack, ActivityRecord target, ActivityOptions targetOptions) { + boolean resumeFocusedTasksTopActivities( + Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions) { if (!mTaskSupervisor.readyToResume()) { return false; } boolean result = false; - if (targetStack != null && (targetStack.isTopStackInDisplayArea() - || getTopDisplayFocusedStack() == targetStack)) { - result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions); + if (targetRootTask != null && (targetRootTask.isTopStackInDisplayArea() + || getTopDisplayFocusedRootTask() == targetRootTask)) { + result = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions); } for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) { @@ -2330,13 +2330,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final boolean curResult = result; boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas( (taskDisplayArea, resumed) -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); - final ActivityRecord topRunningActivity = stack.topRunningActivity(); - if (!stack.isFocusableAndVisible() || topRunningActivity == null) { + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task rootTask = taskDisplayArea.getRootTaskAt(sNdx); + final ActivityRecord topRunningActivity = rootTask.topRunningActivity(); + if (!rootTask.isFocusableAndVisible() || topRunningActivity == null) { continue; } - if (stack == targetStack) { + if (rootTask == targetRootTask) { // Simply update the result for targetStack because the targetStack // had already resumed in above. We don't want to resume it again, // especially in some cases, it would cause a second launch failure @@ -2344,12 +2344,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> resumed |= curResult; continue; } - if (taskDisplayArea.isTopStack(stack) + if (taskDisplayArea.isTopRootTask(rootTask) && topRunningActivity.isState(RESUMED)) { // Kick off any lingering app transitions form the MoveTaskToFront // operation, but only consider the top task and stack on that // display. - stack.executeAppTransition(targetOptions); + rootTask.executeAppTransition(targetOptions); } else { resumed |= topRunningActivity.makeActiveIfNeeded(target); } @@ -2362,10 +2362,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // crashed) it's possible that nothing was resumed on a display. Requesting resume // of top activity in focused stack explicitly will make sure that at least home // activity is started and resumed, and no recursion occurs. - final Task focusedStack = display.getFocusedStack(); - if (focusedStack != null) { - result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions); - } else if (targetStack == null) { + final Task focusedRoot = display.getFocusedRootTask(); + if (focusedRoot != null) { + result |= focusedRoot.resumeTopActivityUncheckedLocked(target, targetOptions); + } else if (targetRootTask == null) { result |= resumeHomeActivity(null /* prev */, "no-focusable-task", display.getDefaultTaskDisplayArea()); } @@ -2391,8 +2391,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Set the sleeping state of the stacks on the display. display.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); if (displayShouldSleep) { stack.goToSleepIfPossible(false /* shuttingDown */); } else { @@ -2419,34 +2419,34 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - protected Task getStack(int stackId) { + protected Task getRootTask(int rooTaskId) { for (int i = getChildCount() - 1; i >= 0; --i) { - final Task stack = getChildAt(i).getStack(stackId); - if (stack != null) { - return stack; + final Task rootTask = getChildAt(i).getRootTask(rooTaskId); + if (rootTask != null) { + return rootTask; } } return null; } - /** @see DisplayContent#getStack(int, int) */ - Task getStack(int windowingMode, int activityType) { + /** @see DisplayContent#getRootTask(int, int) */ + Task getRootTask(int windowingMode, int activityType) { for (int i = getChildCount() - 1; i >= 0; --i) { - final Task stack = getChildAt(i).getStack(windowingMode, activityType); - if (stack != null) { - return stack; + final Task rootTask = getChildAt(i).getRootTask(windowingMode, activityType); + if (rootTask != null) { + return rootTask; } } return null; } - private Task getStack(int windowingMode, int activityType, + private Task getRootTask(int windowingMode, int activityType, int displayId) { DisplayContent display = getDisplayContent(displayId); if (display == null) { return null; } - return display.getStack(windowingMode, activityType); + return display.getRootTask(windowingMode, activityType); } private RootTaskInfo getRootTaskInfo(Task task) { @@ -2491,7 +2491,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } RootTaskInfo getRootTaskInfo(int taskId) { - Task task = getStack(taskId); + Task task = getRootTask(taskId); if (task != null) { return getRootTaskInfo(task); } @@ -2499,12 +2499,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } RootTaskInfo getRootTaskInfo(int windowingMode, int activityType) { - final Task stack = getStack(windowingMode, activityType); + final Task stack = getRootTask(windowingMode, activityType); return (stack != null) ? getRootTaskInfo(stack) : null; } RootTaskInfo getRootTaskInfo(int windowingMode, int activityType, int displayId) { - final Task stack = getStack(windowingMode, activityType, displayId); + final Task stack = getRootTask(windowingMode, activityType, displayId); return (stack != null) ? getRootTaskInfo(stack) : null; } @@ -2513,8 +2513,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> ArrayList<RootTaskInfo> list = new ArrayList<>(); if (displayId == INVALID_DISPLAY) { forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); list.add(getRootTaskInfo(stack)); } }); @@ -2525,8 +2525,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return list; } display.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); list.add(getRootTaskInfo(stack)); } }); @@ -2598,16 +2598,16 @@ class RootWindowContainer extends WindowContainer<DisplayContent> mDisplayManagerInternal.setDisplayAccessUIDs(mDisplayAccessUIDs); } - Task findStackBehind(Task stack) { - final TaskDisplayArea taskDisplayArea = stack.getDisplayArea(); + Task findRootTaskBehind(Task rootTask) { + final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); if (taskDisplayArea != null) { - for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; i--) { - if (taskDisplayArea.getStackAt(i) == stack && i > 0) { - return taskDisplayArea.getStackAt(i - 1); + for (int i = taskDisplayArea.getRootTaskCount() - 1; i >= 0; i--) { + if (taskDisplayArea.getRootTaskAt(i) == rootTask && i > 0) { + return taskDisplayArea.getRootTaskAt(i - 1); } } } - throw new IllegalStateException("Failed to find a stack behind stack=" + stack + throw new IllegalStateException("Failed to find a root task behind root task =" + rootTask + " in=" + taskDisplayArea); } @@ -2743,22 +2743,22 @@ class RootWindowContainer extends WindowContainer<DisplayContent> r.destroyImmediately(mDestroyAllActivitiesReason); } - // Tries to put all activity stacks to sleep. Returns true if all stacks were + // Tries to put all activity tasks to sleep. Returns true if all tasks were // successfully put to sleep. - boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) { + boolean putTasksToSleep(boolean allowDelay, boolean shuttingDown) { return reduceOnAllTaskDisplayAreas((taskDisplayArea, result) -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { // Stacks and activities could be removed while putting activities to sleep if // the app process was gone. This prevents us getting exception by accessing an // invalid stack index. - if (sNdx >= taskDisplayArea.getStackCount()) { + if (sNdx >= taskDisplayArea.getRootTaskCount()) { continue; } - final Task stack = taskDisplayArea.getStackAt(sNdx); + final Task task = taskDisplayArea.getRootTaskAt(sNdx); if (allowDelay) { - result &= stack.goToSleepIfPossible(shuttingDown); + result &= task.goToSleepIfPossible(shuttingDown); } else { - stack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + task.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, !PRESERVE_WINDOWS); } } @@ -2827,9 +2827,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return false; } - Task getLaunchStack(@Nullable ActivityRecord r, + Task getLaunchRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop) { - return getLaunchStack(r, options, candidateTask, onTop, null /* launchParams */, + return getLaunchRootTask(r, options, candidateTask, onTop, null /* launchParams */, -1 /* no realCallingPid */, -1 /* no realCallingUid */); } @@ -2844,7 +2844,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid} * @return The stack to use for the launch or INVALID_STACK_ID. */ - Task getLaunchStack(@Nullable ActivityRecord r, + Task getLaunchRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable Task candidateTask, boolean onTop, @Nullable LaunchParamsController.LaunchParams launchParams, int realCallingPid, int realCallingUid) { @@ -2897,7 +2897,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> realCallingPid, realCallingUid, r.info); if (canLaunchOnDisplayFromStartRequest || canLaunchOnDisplay(r, tdaDisplayId)) { if (r != null) { - final Task result = getValidLaunchStackInTaskDisplayArea( + final Task result = getValidLaunchRootTaskInTaskDisplayArea( taskDisplayArea, r, candidateTask, options, launchParams); if (result != null) { return result; @@ -2905,7 +2905,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } // Falling back to default task container taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea(); - stack = taskDisplayArea.getOrCreateStack(r, options, candidateTask, activityType, + stack = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, activityType, onTop); if (stack != null) { return stack; @@ -2959,7 +2959,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } - return container.getOrCreateStack(r, options, candidateTask, activityType, onTop); + return container.getOrCreateRootTask(r, options, candidateTask, activityType, onTop); } /** @return true if activity record is null or can be launched on provided display. */ @@ -2980,7 +2980,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null. */ @VisibleForTesting - Task getValidLaunchStackInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea, + Task getValidLaunchRootTaskInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea, @NonNull ActivityRecord r, @Nullable Task candidateTask, @Nullable ActivityOptions options, @Nullable LaunchParamsController.LaunchParams launchParams) { @@ -3019,9 +3019,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> r.getActivityType()); // Return the topmost valid stack on the display. - for (int i = taskDisplayArea.getStackCount() - 1; i >= 0; --i) { - final Task stack = taskDisplayArea.getStackAt(i); - if (isValidLaunchStack(stack, r, windowingMode)) { + for (int i = taskDisplayArea.getRootTaskCount() - 1; i >= 0; --i) { + final Task stack = taskDisplayArea.getRootTaskAt(i); + if (isValidLaunchRootTask(stack, r, windowingMode)) { return stack; } } @@ -3033,15 +3033,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final int activityType = options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED ? options.getLaunchActivityType() : r.getActivityType(); - return taskDisplayArea.createStack(windowingMode, activityType, true /*onTop*/); + return taskDisplayArea.createRootTask(windowingMode, activityType, true /*onTop*/); } return null; } // TODO: Can probably be consolidated into getLaunchStack()... - private boolean isValidLaunchStack(Task stack, ActivityRecord r, int windowingMode) { - switch (stack.getActivityType()) { + private boolean isValidLaunchRootTask(Task task, ActivityRecord r, int windowingMode) { + switch (task.getActivityType()) { case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome(); case ACTIVITY_TYPE_RECENTS: @@ -3051,13 +3051,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent> case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream(); } - if (stack.mCreatedByOrganizer) { + if (task.mCreatedByOrganizer) { // Don't launch directly into task created by organizer...but why can't we? return false; } // There is a 1-to-1 relationship between stack and task when not in // primary split-windowing mode. - if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && r.supportsSplitScreenWindowingMode() && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || windowingMode == WINDOWING_MODE_UNDEFINED)) { @@ -3094,8 +3094,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * candidate. * @return Next focusable {@link Task}, {@code null} if not found. */ - Task getNextFocusableStack(@NonNull Task currentFocus, - boolean ignoreCurrent) { + Task getNextFocusableRootTask(@NonNull Task currentFocus, boolean ignoreCurrent) { // First look for next focusable stack on the same display TaskDisplayArea preferredDisplayArea = currentFocus.getDisplayArea(); if (preferredDisplayArea == null) { @@ -3104,7 +3103,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> preferredDisplayArea = getDisplayContent(currentFocus.mPrevDisplayId) .getDefaultTaskDisplayArea(); } - final Task preferredFocusableStack = preferredDisplayArea.getNextFocusableStack( + final Task preferredFocusableStack = preferredDisplayArea.getNextFocusableRootTask( currentFocus, ignoreCurrent); if (preferredFocusableStack != null) { return preferredFocusableStack; @@ -3124,7 +3123,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> continue; } final Task nextFocusableStack = display.getDefaultTaskDisplayArea() - .getNextFocusableStack(currentFocus, ignoreCurrent); + .getNextFocusableRootTask(currentFocus, ignoreCurrent); if (nextFocusableStack != null) { return nextFocusableStack; } @@ -3260,9 +3259,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void finishVoiceTask(IVoiceInteractionSession session) { forAllTaskDisplayAreas(taskDisplayArea -> { - final int numStacks = taskDisplayArea.getStackCount(); + final int numStacks = taskDisplayArea.getRootTaskCount(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { - final Task stack = taskDisplayArea.getStackAt(stackNdx); + final Task stack = taskDisplayArea.getRootTaskAt(stackNdx); stack.finishVoiceTask(session); } }); @@ -3305,7 +3304,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // If the focused stack is not null or not empty, there should have some activities // resuming or resumed. Make sure these activities are idle. - final Task stack = display.getFocusedStack(); + final Task stack = display.getFocusedRootTask(); if (stack == null || !stack.hasActivity()) { continue; } @@ -3325,8 +3324,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean[] foundResumed = {false}; final boolean foundInvisibleResumedActivity = forAllTaskDisplayAreas( taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); final ActivityRecord r = stack.getResumedActivity(); if (r != null) { if (!r.nowVisible) { @@ -3347,8 +3346,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> boolean[] pausing = {true}; final boolean hasActivityNotCompleted = forAllTaskDisplayAreas( taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); final ActivityRecord r = stack.mPausingActivity; if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) { ProtoLog.d(WM_DEBUG_STATES, "allPausedActivitiesComplete: " @@ -3413,10 +3412,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void cancelInitializingActivities() { forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { // We don't want to clear starting window for activities that aren't occluded // as we need to display their starting window until they are done initializing. - taskDisplayArea.getStackAt(sNdx).forAllOccludedActivities( + taskDisplayArea.getRootTaskAt(sNdx).forAllOccludedActivities( ActivityRecord::cancelInitializing); } }); @@ -3456,7 +3455,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Resolve the stack the task should be placed in now based on options // and reparent if needed. final Task launchStack = - getLaunchStack(null, aOptions, task, onTop); + getLaunchRootTask(null, aOptions, task, onTop); if (launchStack != null && task.getRootTask() != launchStack) { final int reparentMode = onTop ? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE; @@ -3501,7 +3500,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return task; } - ActivityRecord isInAnyStack(IBinder token) { + ActivityRecord isInAnyTask(IBinder token) { final ActivityRecord r = ActivityRecord.forTokenLocked(token); return (r != null && r.isDescendantOf(this)) ? r : null; } @@ -3571,7 +3570,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) { if (dumpFocusedStackOnly) { - final Task topFocusedStack = getTopDisplayFocusedStack(); + final Task topFocusedStack = getTopDisplayFocusedRootTask(); if (topFocusedStack != null) { return topFocusedStack.getDumpActivitiesLocked(name); } else { @@ -3580,8 +3579,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } else { ArrayList<ActivityRecord> activities = new ArrayList<>(); forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) { activities.addAll(stack.getDumpActivitiesLocked(name)); } @@ -3595,7 +3594,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> public void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); pw.print(prefix); - pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack()); + pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedRootTask()); for (int i = getChildCount() - 1; i >= 0; --i) { final DisplayContent display = getChildAt(i); display.dump(pw, prefix, dumpAll); @@ -3635,8 +3634,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> pw.print(displayContent.mDisplayId); pw.println(" (activities from top to bottom):"); displayContent.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); if (needSep[0]) { pw.println(); } diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 7b5b0ad870dd..6ef5d4da63cb 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -67,7 +67,7 @@ class RunningTasks { mProfileIds = profileIds; mAllowed = allowed; mFilterOnlyVisibleRecents = filterOnlyVisibleRecents; - mTopDisplayFocusStack = root.getTopDisplayFocusedStack(); + mTopDisplayFocusStack = root.getTopDisplayFocusedRootTask(); mRecentTasks = root.mService.getRecentTasks(); final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this, diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index b46e796698e4..c414c6421dc8 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -38,7 +38,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITION import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.Nullable; -import android.app.ActivityManagerInternal; import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipDescription; @@ -56,6 +55,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.os.UserHandle; +import android.service.attestation.ImpressionToken; import android.text.TextUtils; import android.util.ArraySet; import android.util.MergedConfiguration; @@ -848,4 +848,15 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { Binder.restoreCallingIdentity(identity); } } + + @Override + public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow, + String hashAlgorithm) { + final long origId = Binder.clearCallingIdentity(); + try { + return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm); + } finally { + Binder.restoreCallingIdentity(origId); + } + } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ebf5989d6b55..4b65ce0f7088 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -101,7 +101,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_STACK_MSG; +import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; @@ -196,6 +196,8 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.RemoteAnimationAdapter; @@ -597,7 +599,7 @@ class Task extends WindowContainer<WindowContainer> { private final AnimatingActivityRegistry mAnimatingActivityRegistry = new AnimatingActivityRegistry(); - private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1; + private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_TASK_MSG + 1; private final Handler mHandler; @@ -650,7 +652,7 @@ class Task extends WindowContainer<WindowContainer> { if (mUpdateConfig) { // Ensure the resumed state of the focus activity if we updated the configuration of // any activity. - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } @@ -973,7 +975,7 @@ class Task extends WindowContainer<WindowContainer> { } mResizeMode = resizeMode; mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); updateTaskDescription(); } @@ -1028,7 +1030,7 @@ class Task extends WindowContainer<WindowContainer> { // activities stay the same. mRootWindowContainer.ensureActivitiesVisible(r, 0, preserveWindow); if (!kept) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } } @@ -1096,7 +1098,7 @@ class Task extends WindowContainer<WindowContainer> { final RootWindowContainer root = mRootWindowContainer; final WindowManagerService windowManager = mAtmService.mWindowManager; final Task sourceStack = getRootTask(); - final Task toStack = supervisor.getReparentTargetStack(this, preferredStack, + final Task toStack = supervisor.getReparentTargetRootTask(this, preferredStack, position == MAX_VALUE); if (toStack == sourceStack) { return false; @@ -1134,7 +1136,7 @@ class Task extends WindowContainer<WindowContainer> { boolean kept = true; try { final ActivityRecord r = topRunningActivityLocked(); - final boolean wasFocused = r != null && root.isTopDisplayFocusedStack(sourceStack) + final boolean wasFocused = r != null && root.isTopDisplayFocusedRootTask(sourceStack) && (topRunningActivityLocked() == r); final boolean wasResumed = r != null && sourceStack.getResumedActivity() == r; final boolean wasPaused = r != null && sourceStack.mPausingActivity == r; @@ -1174,7 +1176,7 @@ class Task extends WindowContainer<WindowContainer> { && moveStackMode == REPARENT_KEEP_ROOT_TASK_AT_FRONT) { // Move recents to front so it is not behind home stack when going into docked // mode - mTaskSupervisor.moveRecentsStackToFront(reason); + mTaskSupervisor.moveRecentsRootTaskToFront(reason); } } finally { mAtmService.continueWindowLayout(); @@ -1191,7 +1193,7 @@ class Task extends WindowContainer<WindowContainer> { // The task might have already been running and its visibility needs to be synchronized // with the visibility of the stack / windows. root.ensureActivitiesVisible(null, 0, !mightReplaceWindow); - root.resumeFocusedStacksTopActivities(); + root.resumeFocusedTasksTopActivities(); } // TODO: Handle incorrect request to move before the actual move, not after. @@ -1694,7 +1696,7 @@ class Task extends WindowContainer<WindowContainer> { // A rootable task that is now being added to be the child of an organized task. Making // sure the stack references is keep updated. if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) { - getDisplayArea().addStackReferenceIfNeeded((Task) child); + getDisplayArea().addRootTaskReferenceIfNeeded((Task) child); } // Make sure the list of display UID allowlists is updated @@ -1744,7 +1746,7 @@ class Task extends WindowContainer<WindowContainer> { // A rootable child task that is now being removed from an organized task. Making sure // the stack references is keep updated. if (mCreatedByOrganizer && r.asTask() != null) { - getDisplayArea().removeStackReferenceIfNeeded((Task) r); + getDisplayArea().removeRootTaskReferenceIfNeeded((Task) r); } if (!mChildren.contains(r)) { Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this); @@ -2249,7 +2251,7 @@ class Task extends WindowContainer<WindowContainer> { // the rotation animation needs to capture snapshot earlier to avoid animating from // an intermediate state. if (oldOrientation != getOrientation()) { - onDescendantOrientationChanged(null, this); + onDescendantOrientationChanged(this); } } finally { if (pipChanging) { @@ -2301,7 +2303,7 @@ class Task extends WindowContainer<WindowContainer> { } if (prevWindowingMode != getWindowingMode()) { - taskDisplayArea.onStackWindowingModeChanged(this); + taskDisplayArea.onRootTaskWindowingModeChanged(this); } if (mDisplayContent == null) { @@ -2328,7 +2330,7 @@ class Task extends WindowContainer<WindowContainer> { } if (windowingModeChanged) { - taskDisplayArea.onStackWindowingModeChanged(this); + taskDisplayArea.onRootTaskWindowingModeChanged(this); } if (hasNewOverrideBounds) { if (inSplitScreenWindowingMode()) { @@ -3095,7 +3097,7 @@ class Task extends WindowContainer<WindowContainer> { boolean moveDisplayToTop) { Task focusableTask = getNextFocusableTask(allowFocusSelf); if (focusableTask == null) { - focusableTask = mRootWindowContainer.getNextFocusableStack(this, !allowFocusSelf); + focusableTask = mRootWindowContainer.getNextFocusableRootTask(this, !allowFocusSelf); } if (focusableTask == null) { return null; @@ -3305,9 +3307,8 @@ class Task extends WindowContainer<WindowContainer> { } @Override - public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken, - WindowContainer requestingContainer) { - if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) { + public boolean onDescendantOrientationChanged(WindowContainer requestingContainer) { + if (super.onDescendantOrientationChanged(requestingContainer)) { return true; } @@ -4099,6 +4100,7 @@ class Task extends WindowContainer<WindowContainer> { ? rootTask.mTaskId : INVALID_TASK_ID; info.isFocused = isFocused(); + info.isVisible = hasVisibleChildren(); } @Nullable PictureInPictureParams getPictureInPictureParams() { @@ -4483,14 +4485,14 @@ class Task extends WindowContainer<WindowContainer> { /** * Saves this {@link Task} to XML using given serializer. */ - void saveToXml(XmlSerializer out) throws Exception { + void saveToXml(TypedXmlSerializer out) throws Exception { if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this); - out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId)); + out.attributeInt(null, ATTR_TASKID, mTaskId); if (realActivity != null) { out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString()); } - out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended)); + out.attributeBoolean(null, ATTR_REALACTIVITY_SUSPENDED, realActivitySuspended); if (origActivity != null) { out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString()); } @@ -4509,37 +4511,36 @@ class Task extends WindowContainer<WindowContainer> { if (mWindowLayoutAffinity != null) { out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity); } - out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset)); - out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents)); - out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode)); - out.attribute(null, ATTR_USERID, String.valueOf(mUserId)); - out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete)); - out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid)); - out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved)); - out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity)); + out.attributeBoolean(null, ATTR_ROOTHASRESET, rootWasReset); + out.attributeBoolean(null, ATTR_AUTOREMOVERECENTS, autoRemoveRecents); + out.attributeBoolean(null, ATTR_ASKEDCOMPATMODE, askedCompatMode); + out.attributeInt(null, ATTR_USERID, mUserId); + out.attributeBoolean(null, ATTR_USER_SETUP_COMPLETE, mUserSetupComplete); + out.attributeInt(null, ATTR_EFFECTIVE_UID, effectiveUid); + out.attributeLong(null, ATTR_LASTTIMEMOVED, mLastTimeMoved); + out.attributeBoolean(null, ATTR_NEVERRELINQUISH, mNeverRelinquishIdentity); if (lastDescription != null) { out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString()); } if (getTaskDescription() != null) { getTaskDescription().saveToXml(out); } - out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId)); - out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId)); - out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId)); - out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid)); + out.attributeInt(null, ATTR_TASK_AFFILIATION, mAffiliatedTaskId); + out.attributeInt(null, ATTR_PREV_AFFILIATION, mPrevAffiliateTaskId); + out.attributeInt(null, ATTR_NEXT_AFFILIATION, mNextAffiliateTaskId); + out.attributeInt(null, ATTR_CALLING_UID, mCallingUid); out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage); out.attribute(null, ATTR_CALLING_FEATURE_ID, mCallingFeatureId == null ? "" : mCallingFeatureId); - out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode)); - out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, - String.valueOf(mSupportsPictureInPicture)); + out.attributeInt(null, ATTR_RESIZE_MODE, mResizeMode); + out.attributeBoolean(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, mSupportsPictureInPicture); if (mLastNonFullscreenBounds != null) { out.attribute( null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString()); } - out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth)); - out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight)); - out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION)); + out.attributeInt(null, ATTR_MIN_WIDTH, mMinWidth); + out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight); + out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION); if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); @@ -4564,7 +4565,7 @@ class Task extends WindowContainer<WindowContainer> { } private static boolean saveActivityToXml( - ActivityRecord r, ActivityRecord first, XmlSerializer out) { + ActivityRecord r, ActivityRecord first, TypedXmlSerializer out) { if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) @@ -4583,7 +4584,7 @@ class Task extends WindowContainer<WindowContainer> { } } - static Task restoreFromXml(XmlPullParser in, ActivityTaskSupervisor taskSupervisor) + static Task restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException { Intent intent = null; Intent affinityIntent = null; @@ -5132,7 +5133,7 @@ class Task extends WindowContainer<WindowContainer> { // The change in force-hidden state will change visibility without triggering a stack // order change, so we should reset the preferred top focusable stack to ensure it's not // used if a new activity is started from this task. - getDisplayArea().resetPreferredTopFocusableStackIfBelow(this); + getDisplayArea().resetPreferredTopFocusableRootTaskIfBelow(this); } return true; } @@ -5275,14 +5276,14 @@ class Task extends WindowContainer<WindowContainer> { } mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } /** Resume next focusable stack after reparenting to another display. */ void postReparent() { adjustFocusToNextFocusableTask("reparent", true /* allowFocusSelf */, true /* moveDisplayToTop */); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); // Update visibility of activities before notifying WM. This way it won't try to resize // windows that are no longer visible. mRootWindowContainer.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, @@ -5319,7 +5320,7 @@ class Task extends WindowContainer<WindowContainer> { // cutting between them. // TODO(b/70677280): This is a workaround until we can fix as part of b/70677280. final Task topFullScreenStack = - taskDisplayArea.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); + taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN); if (topFullScreenStack != null) { final Task primarySplitScreenStack = taskDisplayArea.getRootSplitScreenPrimaryTask(); @@ -5334,10 +5335,10 @@ class Task extends WindowContainer<WindowContainer> { if (!isActivityTypeHome() && returnsToHomeStack()) { // Make sure the home stack is behind this stack since that is where we should return to // when this stack is no longer visible. - taskDisplayArea.moveHomeStackToFront(reason + " returnToHome"); + taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome"); } - final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedStack() : null; + final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null; if (task == null) { task = this; } @@ -5365,7 +5366,7 @@ class Task extends WindowContainer<WindowContainer> { if (parentTask != null) { parentTask.moveToBack(reason, this); } else { - final Task lastFocusedTask = displayArea.getFocusedStack(); + final Task lastFocusedTask = displayArea.getFocusedRootTask(); displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/); displayArea.updateLastFocusedRootTask(lastFocusedTask, reason); } @@ -5514,7 +5515,7 @@ class Task extends WindowContainer<WindowContainer> { if (prev == null) { if (resuming == null) { Slog.wtf(TAG, "Trying to pause when nothing is resumed"); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } return false; } @@ -5621,7 +5622,7 @@ class Task extends WindowContainer<WindowContainer> { // pause, so just treat it as being paused now. ProtoLog.v(WM_DEBUG_STATES, "Activity not running, resuming next."); if (resuming == null) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } return false; } @@ -5674,9 +5675,9 @@ class Task extends WindowContainer<WindowContainer> { } if (resumeNext) { - final Task topStack = mRootWindowContainer.getTopDisplayFocusedStack(); + final Task topStack = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topStack != null && !topStack.shouldSleepOrShutDownActivities()) { - mRootWindowContainer.resumeFocusedStacksTopActivities(topStack, prev, null); + mRootWindowContainer.resumeFocusedTasksTopActivities(topStack, prev, null); } else { checkReadyForSleep(); final ActivityRecord top = topStack != null ? topStack.topRunningActivity() : null; @@ -5685,7 +5686,7 @@ class Task extends WindowContainer<WindowContainer> { // something. Also if the top activity on the stack is not the just paused // activity, we need to go ahead and resume it to ensure we complete an // in-flight app switch. - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } } @@ -5708,7 +5709,7 @@ class Task extends WindowContainer<WindowContainer> { boolean isTopStackInDisplayArea() { final TaskDisplayArea taskDisplayArea = getDisplayArea(); - return taskDisplayArea != null && taskDisplayArea.isTopStack(this); + return taskDisplayArea != null && taskDisplayArea.isTopRootTask(this); } /** @@ -5716,7 +5717,7 @@ class Task extends WindowContainer<WindowContainer> { * otherwise. */ boolean isFocusedStackOnDisplay() { - return mDisplayContent != null && this == mDisplayContent.getFocusedStack(); + return mDisplayContent != null && this == mDisplayContent.getFocusedRootTask(); } /** @@ -5764,6 +5765,10 @@ class Task extends WindowContainer<WindowContainer> { starting, configChanges, preserveWindows, notifyClients, userLeaving), true /* traverseTopToBottom */); + // Notify WM shell that task visibilities may have changed + forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false), + true /* traverseTopToBottom */); + if (mTranslucentActivityWaiting != null && mUndrawnActivitiesBelowTopTranslucent.isEmpty()) { // Nothing is getting drawn or everything was already visible, don't wait for @@ -5790,7 +5795,7 @@ class Task extends WindowContainer<WindowContainer> { */ boolean isTopSplitScreenStack() { return inSplitScreenWindowingMode() - && this == getDisplayArea().getTopStackInWindowingMode(getWindowingMode()); + && this == getDisplayArea().getTopRootTaskInWindowingMode(getWindowingMode()); } void checkTranslucentActivityWaiting(ActivityRecord top) { @@ -5874,7 +5879,7 @@ class Task extends WindowContainer<WindowContainer> { * * NOTE: It is not safe to call this method directly as it can cause an activity in a * non-focused stack to be resumed. - * Use {@link RootWindowContainer#resumeFocusedStacksTopActivities} to resume the + * Use {@link RootWindowContainer#resumeFocusedTasksTopActivities} to resume the * right activity for the current system state. */ @GuardedBy("mService") @@ -6028,7 +6033,7 @@ class Task extends WindowContainer<WindowContainer> { mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid); ActivityRecord lastResumed = null; - final Task lastFocusedStack = taskDisplayArea.getLastFocusedStack(); + final Task lastFocusedStack = taskDisplayArea.getLastFocusedRootTask(); if (lastFocusedStack != null && lastFocusedStack != this) { // So, why aren't we using prev here??? See the param comment on the method. prev // doesn't represent the last resumed activity. However, the last focus stack does if @@ -6043,7 +6048,7 @@ class Task extends WindowContainer<WindowContainer> { } } - boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next); + boolean pausing = taskDisplayArea.pauseBackTasks(userLeaving, next); if (mResumedActivity != null) { ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Pausing %s", mResumedActivity); pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next, @@ -6064,7 +6069,7 @@ class Task extends WindowContainer<WindowContainer> { // Since the start-process is asynchronous, if we already know the process of next // activity isn't running, we can start the process earlier to save the time to wait // for the current activity to be paused. - final boolean isTop = this == taskDisplayArea.getFocusedStack(); + final boolean isTop = this == taskDisplayArea.getFocusedRootTask(); mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop, isTop ? "pre-top-activity" : "pre-activity"); } @@ -6354,7 +6359,7 @@ class Task extends WindowContainer<WindowContainer> { // Try to move focus to the next visible stack with a running activity if this // stack is not covering the entire screen or is on a secondary display with no home // stack. - return mRootWindowContainer.resumeFocusedStacksTopActivities(nextFocusedStack, + return mRootWindowContainer.resumeFocusedTasksTopActivities(nextFocusedStack, prev, null /* targetOptions */); } } @@ -6834,7 +6839,7 @@ class Task extends WindowContainer<WindowContainer> { AppTimeTracker timeTracker, boolean deferResume, String reason) { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr); - final Task topStack = getDisplayArea().getTopStack(); + final Task topStack = getDisplayArea().getTopRootTask(); final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null; @@ -6897,7 +6902,7 @@ class Task extends WindowContainer<WindowContainer> { } if (!deferResume) { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } } finally { mDisplayContent.continueUpdateImeTarget(); @@ -6970,7 +6975,7 @@ class Task extends WindowContainer<WindowContainer> { // resumed in this case, so we need to execute it explicitly. mDisplayContent.executeAppTransition(); } else { - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } return true; } @@ -7275,7 +7280,7 @@ class Task extends WindowContainer<WindowContainer> { final boolean wasResumed = topRunningActivity == task.getRootTask().mResumedActivity; boolean toTop = position >= getChildCount(); - boolean includingParents = toTop || getDisplayArea().getNextFocusableStack(this, + boolean includingParents = toTop || getDisplayArea().getNextFocusableRootTask(this, true /* ignoreCurrent */) == null; if (WindowManagerDebugConfig.DEBUG_ROOT_TASK) { Slog.i(TAG_WM, "positionChildAt: positioning task=" + task + " at " + position); @@ -7304,7 +7309,7 @@ class Task extends WindowContainer<WindowContainer> { // The task might have already been running and its visibility needs to be synchronized with // the visibility of the stack / windows. ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); } public void setAlwaysOnTop(boolean alwaysOnTop) { @@ -7426,7 +7431,7 @@ class Task extends WindowContainer<WindowContainer> { // If there are other focusable stacks on the display, the z-order of the display should not // be changed just because a task was placed at the bottom. E.g. if it is moving the topmost // task to bottom, the next focusable stack on the same display should be focused. - final Task nextFocusableStack = getDisplayArea().getNextFocusableStack( + final Task nextFocusableStack = getDisplayArea().getNextFocusableRootTask( child.getRootTask(), true /* ignoreCurrent */); positionChildAtBottom(child, nextFocusableStack == null /* includingParents */); } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 4682ba836426..b4d069c0edc1 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -193,7 +193,7 @@ class TaskChangeNotificationController { switch (msg.what) { case LOG_STACK_STATE_MSG: { synchronized (mServiceLock) { - mTaskSupervisor.logStackState(); + mTaskSupervisor.logRootTaskState(); } break; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 81b8200aa2b4..4498a8c82583 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -38,7 +38,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK; -import static com.android.server.wm.DisplayContent.alwaysCreateStack; +import static com.android.server.wm.DisplayContent.alwaysCreateRootTask; import static com.android.server.wm.Task.ActivityState.RESUMED; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK; @@ -107,9 +107,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { // TODO(b/159029784): Remove when getStack() behavior is cleaned-up private Task mRootRecentsTask; - private final ArrayList<Task> mTmpAlwaysOnTopStacks = new ArrayList<>(); - private final ArrayList<Task> mTmpNormalStacks = new ArrayList<>(); - private final ArrayList<Task> mTmpHomeStacks = new ArrayList<>(); + private final ArrayList<Task> mTmpAlwaysOnTopRootTasks = new ArrayList<>(); + private final ArrayList<Task> mTmpNormalRootTasks = new ArrayList<>(); + private final ArrayList<Task> mTmpHomeRootTasks = new ArrayList<>(); private final IntArray mTmpNeedsZBoostIndexes = new IntArray(); private int mTmpLayerForSplitScreenDividerAnchor; private int mTmpLayerForAnimationLayer; @@ -128,23 +128,25 @@ final class TaskDisplayArea extends DisplayArea<Task> { * have the topmost index, it is used as a preferred candidate to prevent being unable to resume * target stack properly when there are other focusable always-on-top stacks. */ - Task mPreferredTopFocusableStack; + Task mPreferredTopFocusableRootTask; private final RootWindowContainer.FindTaskResult mTmpFindTaskResult = new RootWindowContainer.FindTaskResult(); /** - * If this is the same as {@link #getFocusedStack} then the activity on the top of the focused - * stack has been resumed. If stacks are changing position this will hold the old stack until - * the new stack becomes resumed after which it will be set to current focused stack. + * If this is the same as {@link #getFocusedRootTask} then the activity on the top of the + * focused root task has been resumed. If root tasks are changing position this will hold the + * old root task until the new root task becomes resumed after which it will be set to + * current focused root task. */ - Task mLastFocusedStack; + Task mLastFocusedRootTask; /** * All of the stacks on this display. Order matters, topmost stack is in front of all other * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls - * changing the list should also call {@link #onStackOrderChanged()}. + * changing the list should also call {@link #onRootTaskOrderChanged(Task)}. */ - private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>(); + private ArrayList<OnRootTaskOrderChangedListener> mRootTaskOrderChangedCallbacks = + new ArrayList<>(); /** * The task display area is removed from the system and we are just waiting for all activities @@ -181,7 +183,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { * Returns the topmost stack on the display that is compatible with the input windowing mode * and activity type. Null is no compatible stack on the display. */ - Task getStack(int windowingMode, int activityType) { + Task getRootTask(int windowingMode, int activityType) { if (activityType == ACTIVITY_TYPE_HOME) { return mRootHomeTask; } else if (activityType == ACTIVITY_TYPE_RECENTS) { @@ -208,7 +210,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { } @VisibleForTesting - Task getTopStack() { + Task getTopRootTask() { final int count = getChildCount(); return count > 0 ? getChildAt(count - 1) : null; } @@ -255,68 +257,68 @@ final class TaskDisplayArea extends DisplayArea<Task> { return visibleTasks; } - void onStackWindowingModeChanged(Task stack) { - removeStackReferenceIfNeeded(stack); - addStackReferenceIfNeeded(stack); - if (stack == mRootPinnedTask && getTopStack() != stack) { + void onRootTaskWindowingModeChanged(Task rootTask) { + removeRootTaskReferenceIfNeeded(rootTask); + addRootTaskReferenceIfNeeded(rootTask); + if (rootTask == mRootPinnedTask && getTopRootTask() != rootTask) { // Looks like this stack changed windowing mode to pinned. Move it to the top. - positionChildAt(POSITION_TOP, stack, false /* includingParents */); + positionChildAt(POSITION_TOP, rootTask, false /* includingParents */); } } - void addStackReferenceIfNeeded(Task stack) { - if (stack.isActivityTypeHome()) { + void addRootTaskReferenceIfNeeded(Task rootTask) { + if (rootTask.isActivityTypeHome()) { if (mRootHomeTask != null) { - if (!stack.isDescendantOf(mRootHomeTask)) { + if (!rootTask.isDescendantOf(mRootHomeTask)) { throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack=" + mRootHomeTask + " already exist on display=" + this - + " stack=" + stack); + + " stack=" + rootTask); } } else { - mRootHomeTask = stack; + mRootHomeTask = rootTask; } - } else if (stack.isActivityTypeRecents()) { + } else if (rootTask.isActivityTypeRecents()) { if (mRootRecentsTask != null) { - if (!stack.isDescendantOf(mRootRecentsTask)) { + if (!rootTask.isDescendantOf(mRootRecentsTask)) { throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack=" + mRootRecentsTask + " already exist on display=" + this - + " stack=" + stack); + + " stack=" + rootTask); } } else { - mRootRecentsTask = stack; + mRootRecentsTask = rootTask; } } - if (!stack.isRootTask()) { + if (!rootTask.isRootTask()) { return; } - final int windowingMode = stack.getWindowingMode(); + final int windowingMode = rootTask.getWindowingMode(); if (windowingMode == WINDOWING_MODE_PINNED) { if (mRootPinnedTask != null) { throw new IllegalArgumentException( "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask - + " already exist on display=" + this + " stack=" + stack); + + " already exist on display=" + this + " stack=" + rootTask); } - mRootPinnedTask = stack; + mRootPinnedTask = rootTask; } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { if (mRootSplitScreenPrimaryTask != null) { throw new IllegalArgumentException( "addStackReferenceIfNeeded: split screen primary stack=" + mRootSplitScreenPrimaryTask - + " already exist on display=" + this + " stack=" + stack); + + " already exist on display=" + this + " stack=" + rootTask); } - mRootSplitScreenPrimaryTask = stack; + mRootSplitScreenPrimaryTask = rootTask; } } - void removeStackReferenceIfNeeded(Task stack) { - if (stack == mRootHomeTask) { + void removeRootTaskReferenceIfNeeded(Task rootTask) { + if (rootTask == mRootHomeTask) { mRootHomeTask = null; - } else if (stack == mRootRecentsTask) { + } else if (rootTask == mRootRecentsTask) { mRootRecentsTask = null; - } else if (stack == mRootPinnedTask) { + } else if (rootTask == mRootPinnedTask) { mRootPinnedTask = null; - } else if (stack == mRootSplitScreenPrimaryTask) { + } else if (rootTask == mRootSplitScreenPrimaryTask) { mRootSplitScreenPrimaryTask = null; } } @@ -325,20 +327,20 @@ final class TaskDisplayArea extends DisplayArea<Task> { void addChild(Task task, int position) { if (DEBUG_ROOT_TASK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this); - addStackReferenceIfNeeded(task); - position = findPositionForStack(position, task, true /* adding */); + addRootTaskReferenceIfNeeded(task); + position = findPositionForRootTask(position, task, true /* adding */); super.addChild(task, position); mAtmService.updateSleepIfNeededLocked(); - onStackOrderChanged(task); + onRootTaskOrderChanged(task); } @Override protected void removeChild(Task stack) { super.removeChild(stack); - onStackRemoved(stack); + onRootTaskRemoved(stack); mAtmService.updateSleepIfNeededLocked(); - removeStackReferenceIfNeeded(stack); + removeRootTaskReferenceIfNeeded(stack); } @Override @@ -367,7 +369,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { if (!mDisplayContent.isTrusted() && !getParent().isOnTop()) { includingParents = false; } - final int targetPosition = findPositionForStack(position, child, false /* adding */); + final int targetPosition = findPositionForRootTask(position, child, false /* adding */); super.positionChildAt(targetPosition, child, false /* includingParents */); if (includingParents && getParent() != null && (moveToTop || moveToBottom)) { @@ -385,16 +387,16 @@ final class TaskDisplayArea extends DisplayArea<Task> { // preferred stack is set only when moving an existing stack to top instead of adding a new // stack that may be too early (e.g. in the middle of launching or reparenting). if (moveToTop && child.isFocusableAndVisible()) { - mPreferredTopFocusableStack = child; - } else if (mPreferredTopFocusableStack == child) { - mPreferredTopFocusableStack = null; + mPreferredTopFocusableRootTask = child; + } else if (mPreferredTopFocusableRootTask == child) { + mPreferredTopFocusableRootTask = null; } // Update the top resumed activity because the preferred top focusable task may be changed. mAtmService.mTaskSupervisor.updateTopResumedActivityIfNeeded(); if (mChildren.indexOf(child) != oldPosition) { - onStackOrderChanged(child); + onRootTaskOrderChanged(child); } } @@ -469,21 +471,21 @@ final class TaskDisplayArea extends DisplayArea<Task> { return 0; } - private int findMinPositionForStack(Task stack) { + private int findMinPositionForRootTask(Task rootTask) { int minPosition = POSITION_BOTTOM; for (int i = 0; i < mChildren.size(); ++i) { - if (getPriority(getStackAt(i)) < getPriority(stack)) { + if (getPriority(getRootTaskAt(i)) < getPriority(rootTask)) { minPosition = i; } else { break; } } - if (stack.isAlwaysOnTop()) { + if (rootTask.isAlwaysOnTop()) { // Since a stack could be repositioned while still being one of the children, we check // if this always-on-top stack already exists and if so, set the minPosition to its // previous position. - final int currentIndex = getIndexOf(stack); + final int currentIndex = getIndexOf(rootTask); if (currentIndex > minPosition) { minPosition = currentIndex; } @@ -491,13 +493,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { return minPosition; } - private int findMaxPositionForStack(Task stack) { + private int findMaxPositionForRootTask(Task rootTask) { for (int i = mChildren.size() - 1; i >= 0; --i) { - final Task curr = getStackAt(i); + final Task curr = getRootTaskAt(i); // Since a stack could be repositioned while still being one of the children, we check // if 'curr' is the same stack and skip it if so - final boolean sameStack = curr == stack; - if (getPriority(curr) <= getPriority(stack) && !sameStack) { + final boolean sameRootTask = curr == rootTask; + if (getPriority(curr) <= getPriority(rootTask) && !sameRootTask) { return i; } } @@ -519,16 +521,16 @@ final class TaskDisplayArea extends DisplayArea<Task> { * (including the Dream); otherwise, it is a normal non-always-on-top stack * * @param requestedPosition Position requested by caller. - * @param stack Stack to be added or positioned. + * @param rootTask Root task to be added or positioned. * @param adding Flag indicates whether we're adding a new stack or positioning an * existing. * @return The proper position for the stack. */ - private int findPositionForStack(int requestedPosition, Task stack, boolean adding) { + private int findPositionForRootTask(int requestedPosition, Task rootTask, boolean adding) { // The max possible position we can insert the stack at. - int maxPosition = findMaxPositionForStack(stack); + int maxPosition = findMaxPositionForRootTask(rootTask); // The min possible position we can insert the stack at. - int minPosition = findMinPositionForStack(stack); + int minPosition = findMinPositionForRootTask(rootTask); // Cap the requested position to something reasonable for the previous position check // below. @@ -542,7 +544,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { targetPosition = Math.min(targetPosition, maxPosition); targetPosition = Math.max(targetPosition, minPosition); - int prevPosition = mChildren.indexOf(stack); + int prevPosition = mChildren.indexOf(rootTask); // The positions we calculated above (maxPosition, minPosition) do not take into // consideration the following edge cases. // 1) We need to adjust the position depending on the value "adding". @@ -645,7 +647,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { return SCREEN_ORIENTATION_UNSET; } - if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { + if (isRootTaskVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { // Apps and their containers are not allowed to specify an orientation while using // root tasks...except for the home stack if it is not resizable and currently // visible (top of) its root task. @@ -672,7 +674,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { } else { // Apps and their containers are not allowed to specify an orientation of full screen // tasks created by organizer. The organizer handles the orientation instead. - final Task task = getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); + final Task task = getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN); if (task != null && task.isVisible() && task.mCreatedByOrganizer) { return SCREEN_ORIENTATION_UNSPECIFIED; } @@ -697,7 +699,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { @Override void assignChildLayers(SurfaceControl.Transaction t) { - assignStackOrdering(t); + assignRootTaskOrdering(t); for (int i = 0; i < mChildren.size(); i++) { final Task s = mChildren.get(i); @@ -705,37 +707,37 @@ final class TaskDisplayArea extends DisplayArea<Task> { } } - void assignStackOrdering(SurfaceControl.Transaction t) { + void assignRootTaskOrdering(SurfaceControl.Transaction t) { if (getParent() == null) { return; } - mTmpAlwaysOnTopStacks.clear(); - mTmpHomeStacks.clear(); - mTmpNormalStacks.clear(); + mTmpAlwaysOnTopRootTasks.clear(); + mTmpHomeRootTasks.clear(); + mTmpNormalRootTasks.clear(); for (int i = 0; i < mChildren.size(); ++i) { final Task s = mChildren.get(i); if (s.isAlwaysOnTop()) { - mTmpAlwaysOnTopStacks.add(s); + mTmpAlwaysOnTopRootTasks.add(s); } else if (s.isActivityTypeHome()) { - mTmpHomeStacks.add(s); + mTmpHomeRootTasks.add(s); } else { - mTmpNormalStacks.add(s); + mTmpNormalRootTasks.add(s); } } int layer = 0; // Place home stacks to the bottom. - layer = adjustRootTaskLayer(t, mTmpHomeStacks, layer, false /* normalStacks */); + layer = adjustRootTaskLayer(t, mTmpHomeRootTasks, layer, false /* normalStacks */); // The home animation layer is between the home stacks and the normal stacks. final int layerForHomeAnimationLayer = layer++; mTmpLayerForSplitScreenDividerAnchor = layer++; mTmpLayerForAnimationLayer = layer++; - layer = adjustRootTaskLayer(t, mTmpNormalStacks, layer, true /* normalStacks */); + layer = adjustRootTaskLayer(t, mTmpNormalRootTasks, layer, true /* normalStacks */); // The boosted animation layer is between the normal stacks and the always on top // stacks. final int layerForBoostedAnimationLayer = layer++; - adjustRootTaskLayer(t, mTmpAlwaysOnTopStacks, layer, false /* normalStacks */); + adjustRootTaskLayer(t, mTmpAlwaysOnTopRootTasks, layer, false /* normalStacks */); t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer); t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer); @@ -743,7 +745,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer); } - private int adjustNormalStackLayer(Task s, int layer) { + private int adjustNormalRootTaskLayer(Task s, int layer) { if (s.inSplitScreenWindowingMode()) { // The split screen divider anchor is located above the split screen window. mTmpLayerForSplitScreenDividerAnchor = layer++; @@ -773,7 +775,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { if (!stack.needsZBoost()) { stack.assignLayer(t, startLayer++); if (normalStacks) { - startLayer = adjustNormalStackLayer(stack, startLayer); + startLayer = adjustNormalRootTaskLayer(stack, startLayer); } } else { mTmpNeedsZBoostIndexes.add(i); @@ -785,7 +787,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { final Task stack = stacks.get(mTmpNeedsZBoostIndexes.get(i)); stack.assignLayer(t, startLayer++); if (normalStacks) { - startLayer = adjustNormalStackLayer(stack, startLayer); + startLayer = adjustNormalRootTaskLayer(stack, startLayer); } } return startLayer; @@ -849,22 +851,22 @@ final class TaskDisplayArea extends DisplayArea<Task> { } } - void onStackRemoved(Task stack) { + void onRootTaskRemoved(Task rootTask) { if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { - Slog.v(TAG_ROOT_TASK, "removeStack: detaching " + stack + " from displayId=" + Slog.v(TAG_ROOT_TASK, "removeStack: detaching " + rootTask + " from displayId=" + mDisplayContent.mDisplayId); } - if (mPreferredTopFocusableStack == stack) { - mPreferredTopFocusableStack = null; + if (mPreferredTopFocusableRootTask == rootTask) { + mPreferredTopFocusableRootTask = null; } mDisplayContent.releaseSelfIfNeeded(); - onStackOrderChanged(stack); + onRootTaskOrderChanged(rootTask); } - void resetPreferredTopFocusableStackIfBelow(Task task) { - if (mPreferredTopFocusableStack != null - && mPreferredTopFocusableStack.compareTo(task) < 0) { - mPreferredTopFocusableStack = null; + void resetPreferredTopFocusableRootTaskIfBelow(Task task) { + if (mPreferredTopFocusableRootTask != null + && mPreferredTopFocusableRootTask.compareTo(task) < 0) { + mPreferredTopFocusableRootTask = null; } } @@ -894,9 +896,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { } } - Task getStack(int rootTaskId) { - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); + Task getRootTask(int rootTaskId) { + for (int i = getRootTaskCount() - 1; i >= 0; --i) { + final Task stack = getRootTaskAt(i); if (stack.getRootTaskId() == rootTaskId) { return stack; } @@ -908,10 +910,10 @@ final class TaskDisplayArea extends DisplayArea<Task> { * Returns an existing stack compatible with the windowing mode and activity type or creates one * if a compatible stack doesn't exist. * - * @see #getOrCreateStack(int, int, boolean, Intent, Task) + * @see #getOrCreateRootTask(int, int, boolean, Intent, Task) */ - Task getOrCreateStack(int windowingMode, int activityType, boolean onTop) { - return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop) { + return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */, null /* candidateTask */); } @@ -921,17 +923,17 @@ final class TaskDisplayArea extends DisplayArea<Task> { * For one level task, the candidate task would be reused to also be the root task or create * a new root task if no candidate task. * - * @see #getStack(int, int) - * @see #createStack(int, int, boolean) + * @see #getRootTask(int, int) + * @see #createRootTask(int, int, boolean) */ - Task getOrCreateStack(int windowingMode, int activityType, boolean onTop, + Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop, Intent intent, Task candidateTask) { // Need to pass in a determined windowing mode to see if a new stack should be created, // so use its parent's windowing mode if it is undefined. - if (!alwaysCreateStack( + if (!alwaysCreateRootTask( windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(), activityType)) { - Task stack = getStack(windowingMode, activityType); + Task stack = getRootTask(windowingMode, activityType); if (stack != null) { return stack; } @@ -959,7 +961,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { } return stack; } - return createStack(windowingMode, activityType, onTop, null /*info*/, intent, + return createRootTask(windowingMode, activityType, onTop, null /*info*/, intent, false /* createdByOrganizer */); } @@ -967,9 +969,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { * Returns an existing stack compatible with the input params or creates one * if a compatible stack doesn't exist. * - * @see #getOrCreateStack(int, int, boolean) + * @see #getOrCreateRootTask(int, int, boolean) */ - Task getOrCreateStack(@Nullable ActivityRecord r, + Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType, boolean onTop) { // First preference is the windowing mode in the activity options if set. @@ -979,24 +981,24 @@ final class TaskDisplayArea extends DisplayArea<Task> { // UNDEFINED windowing mode is a valid result and means that the new stack will inherit // it's display's windowing mode. windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType); - return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */, + return getOrCreateRootTask(windowingMode, activityType, onTop, null /* intent */, candidateTask); } @VisibleForTesting - int getNextStackId() { + int getNextRootTaskId() { return mAtmService.mTaskSupervisor.getNextTaskIdForUser(); } - Task createStack(int windowingMode, int activityType, boolean onTop) { - return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */, - false /* createdByOrganizer */); + Task createRootTask(int windowingMode, int activityType, boolean onTop) { + return createRootTask(windowingMode, activityType, onTop, null /* info */, + null /* intent */, false /* createdByOrganizer */); } - Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, + Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) { - return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */, - false /* createdByOrganizer */ , false /* deferTaskAppear */, + return createRootTask(windowingMode, activityType, onTop, null /* info */, + null /* intent */, false /* createdByOrganizer */, false /* deferTaskAppear */, null /* launchCookie */); } @@ -1022,7 +1024,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { * creating. * @return The newly created stack. */ - Task createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info, + Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, IBinder launchCookie) { if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) { @@ -1034,7 +1036,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) { // For now there can be only one stack of a particular non-standard activity type on a // display. So, get that ignoring whatever windowing mode it is currently in. - Task stack = getStack(WINDOWING_MODE_UNDEFINED, activityType); + Task stack = getRootTask(WINDOWING_MODE_UNDEFINED, activityType); if (stack != null) { throw new IllegalArgumentException("Stack=" + stack + " of activityType=" + activityType + " already on display=" + this + ". Can't have multiple."); @@ -1054,8 +1056,8 @@ final class TaskDisplayArea extends DisplayArea<Task> { getRootPinnedTask().dismissPip(); } - final int stackId = getNextStackId(); - return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent, + final int stackId = getNextRootTaskId(); + return createRootTaskUnchecked(windowingMode, activityType, stackId, onTop, info, intent, createdByOrganizer, deferTaskAppear, launchCookie); } @@ -1065,15 +1067,15 @@ final class TaskDisplayArea extends DisplayArea<Task> { // Only split-screen windowing modes can do this currently... return null; } - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task t = getStackAt(i); + for (int i = getRootTaskCount() - 1; i >= 0; --i) { + final Task t = getRootTaskAt(i); if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) { continue; } // If not already set, pick a launch root which is not the one we are launching into. if (mLaunchRootTask == null) { - for (int j = 0, n = getStackCount(); j < n; ++j) { - final Task tt = getStackAt(j); + for (int j = 0, n = getRootTaskCount(); j < n; ++j) { + final Task tt = getRootTaskAt(j); if (tt.mCreatedByOrganizer && tt != t) { mLaunchRootTask = tt; break; @@ -1086,7 +1088,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { } @VisibleForTesting - Task createStackUnchecked(int windowingMode, int activityType, int stackId, boolean onTop, + Task createRootTaskUnchecked(int windowingMode, int activityType, int stackId, boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear, IBinder launchCookie) { if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) { @@ -1123,13 +1125,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a * focusable and visible stack from the top of stacks in this display. */ - Task getFocusedStack() { - if (mPreferredTopFocusableStack != null) { - return mPreferredTopFocusableStack; + Task getFocusedRootTask() { + if (mPreferredTopFocusableRootTask != null) { + return mPreferredTopFocusableRootTask; } - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); + for (int i = getRootTaskCount() - 1; i >= 0; --i) { + final Task stack = getRootTaskAt(i); if (stack.isFocusableAndVisible()) { return stack; } @@ -1138,22 +1140,22 @@ final class TaskDisplayArea extends DisplayArea<Task> { return null; } - Task getNextFocusableStack(Task currentFocus, boolean ignoreCurrent) { + Task getNextFocusableRootTask(Task currentFocus, boolean ignoreCurrent) { final int currentWindowingMode = currentFocus != null ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED; Task candidate = null; - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); - if (ignoreCurrent && stack == currentFocus) { + for (int i = getRootTaskCount() - 1; i >= 0; --i) { + final Task rootTask = getRootTaskAt(i); + if (ignoreCurrent && rootTask == currentFocus) { continue; } - if (!stack.isFocusableAndVisible()) { + if (!rootTask.isFocusableAndVisible()) { continue; } if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY - && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) { + && candidate == null && rootTask.inSplitScreenPrimaryWindowingMode()) { // If the currently focused stack is in split-screen secondary we save off the // top primary split-screen stack as a candidate for focus because we might // prefer focus to move to an other stack to avoid primary split-screen stack @@ -1161,20 +1163,20 @@ final class TaskDisplayArea extends DisplayArea<Task> { // than the next split-screen stack. Assistant stack, I am looking at you... // We only move the focus to the primary-split screen stack if there isn't a // better alternative. - candidate = stack; + candidate = rootTask; continue; } - if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) { + if (candidate != null && rootTask.inSplitScreenSecondaryWindowingMode()) { // Use the candidate stack since we are now at the secondary split-screen. return candidate; } - return stack; + return rootTask; } return candidate; } ActivityRecord getFocusedActivity() { - final Task focusedStack = getFocusedStack(); + final Task focusedStack = getFocusedRootTask(); if (focusedStack == null) { return null; } @@ -1194,8 +1196,8 @@ final class TaskDisplayArea extends DisplayArea<Task> { return resumedActivity; } - Task getLastFocusedStack() { - return mLastFocusedStack; + Task getLastFocusedRootTask() { + return mLastFocusedRootTask; } void updateLastFocusedRootTask(Task prevFocusedTask, String updateLastFocusedTaskReason) { @@ -1203,7 +1205,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { return; } - final Task currentFocusedTask = getFocusedStack(); + final Task currentFocusedTask = getFocusedRootTask(); if (currentFocusedTask == prevFocusedTask) { return; } @@ -1214,27 +1216,27 @@ final class TaskDisplayArea extends DisplayArea<Task> { currentFocusedTask.mLastPausedActivity = null; } - mLastFocusedStack = prevFocusedTask; + mLastFocusedRootTask = prevFocusedTask; EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser, mDisplayContent.mDisplayId, currentFocusedTask == null ? -1 : currentFocusedTask.getRootTaskId(), - mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(), + mLastFocusedRootTask == null ? -1 : mLastFocusedRootTask.getRootTaskId(), updateLastFocusedTaskReason); } boolean allResumedActivitiesComplete() { - for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { - final ActivityRecord r = getStackAt(stackNdx).getResumedActivity(); + for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) { + final ActivityRecord r = getRootTaskAt(stackNdx).getResumedActivity(); if (r != null && !r.isState(RESUMED)) { return false; } } - final Task currentFocusedStack = getFocusedStack(); + final Task currentFocusedStack = getFocusedRootTask(); if (ActivityTaskManagerDebugConfig.DEBUG_ROOT_TASK) { Slog.d(TAG_ROOT_TASK, "allResumedActivitiesComplete: mLastFocusedStack changing from=" - + mLastFocusedStack + " to=" + currentFocusedStack); + + mLastFocusedRootTask + " to=" + currentFocusedStack); } - mLastFocusedStack = currentFocusedStack; + mLastFocusedRootTask = currentFocusedStack; return true; } @@ -1249,10 +1251,10 @@ final class TaskDisplayArea extends DisplayArea<Task> { * @param resuming The resuming activity. * @return {@code true} if any activity was paused as a result of this call. */ - boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) { + boolean pauseBackTasks(boolean userLeaving, ActivityRecord resuming) { boolean someActivityPaused = false; - for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { - final Task stack = getStackAt(stackNdx); + for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) { + final Task stack = getRootTaskAt(stackNdx); final ActivityRecord resumedActivity = stack.getResumedActivity(); if (resumedActivity != null && (stack.getVisibility(resuming) != TASK_VISIBILITY_VISIBLE @@ -1272,8 +1274,8 @@ final class TaskDisplayArea extends DisplayArea<Task> { void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplayArea, RootWindowContainer.FindTaskResult result) { mTmpFindTaskResult.clear(); - for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { - final Task stack = getStackAt(stackNdx); + for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) { + final Task stack = getRootTaskAt(stackNdx); if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) { ProtoLog.d(WM_DEBUG_TASKS, "Skipping stack: (mismatch activity/stack) " + "%s", stack); @@ -1367,7 +1369,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { // The focused task could be a non-resizeable fullscreen root task that is on top of the // other split-screen tasks, therefore had to dismiss split-screen, make sure the current // focused root task can still be on top after dismissal - final Task rootTask = getFocusedStack(); + final Task rootTask = getFocusedRootTask(); final Task toTop = rootTask != null && !rootTask.inSplitScreenWindowingMode() ? rootTask : null; onSplitScreenModeDismissed(toTop); @@ -1380,9 +1382,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { moveSplitScreenTasksToFullScreen(); } finally { final Task topFullscreenStack = toTop != null - ? toTop : getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN); + ? toTop : getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN); final Task homeStack = getOrCreateRootHomeTask(); - if (homeStack != null && ((topFullscreenStack != null && !isTopStack(homeStack)) + if (homeStack != null && ((topFullscreenStack != null && !isTopRootTask(homeStack)) || toTop != null)) { // Whenever split-screen is dismissed we want the home stack directly behind the // current top fullscreen stack so it shows up when the top stack is finished. @@ -1556,8 +1558,8 @@ final class TaskDisplayArea extends DisplayArea<Task> { return windowingMode; } - boolean isTopStack(Task stack) { - return stack == getTopStack(); + boolean isTopRootTask(Task stack) { + return stack == getTopRootTask(); } ActivityRecord topRunningActivity() { @@ -1575,15 +1577,15 @@ final class TaskDisplayArea extends DisplayArea<Task> { */ ActivityRecord topRunningActivity(boolean considerKeyguardState) { ActivityRecord topRunning = null; - final Task focusedStack = getFocusedStack(); + final Task focusedStack = getFocusedRootTask(); if (focusedStack != null) { topRunning = focusedStack.topRunningActivity(); } // Look in other focusable stacks. if (topRunning == null) { - for (int i = getStackCount() - 1; i >= 0; --i) { - final Task stack = getStackAt(i); + for (int i = getRootTaskCount() - 1; i >= 0; --i) { + final Task stack = getRootTaskAt(i); // Only consider focusable stacks other than the current focused one. if (stack == focusedStack || !stack.isTopActivityFocusable()) { continue; @@ -1607,13 +1609,11 @@ final class TaskDisplayArea extends DisplayArea<Task> { return topRunning; } - // TODO (b/157876447): switch to Task related name - protected int getStackCount() { + protected int getRootTaskCount() { return mChildren.size(); } - // TODO (b/157876447): switch to Task related name - protected Task getStackAt(int index) { + protected Task getRootTaskAt(int index) { return mChildren.get(index); } @@ -1633,7 +1633,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { Task getOrCreateRootHomeTask(boolean onTop) { Task homeTask = getRootHomeTask(); if (homeTask == null && mDisplayContent.supportsSystemDecorations()) { - homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop); + homeTask = createRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop); } return homeTask; } @@ -1647,14 +1647,14 @@ final class TaskDisplayArea extends DisplayArea<Task> { * Returns the topmost stack on the display that is compatible with the input windowing mode. * Null is no compatible stack on the display. */ - Task getTopStackInWindowingMode(int windowingMode) { - return getStack(windowingMode, ACTIVITY_TYPE_UNDEFINED); + Task getTopRootTaskInWindowingMode(int windowingMode) { + return getRootTask(windowingMode, ACTIVITY_TYPE_UNDEFINED); } - void moveHomeStackToFront(String reason) { - final Task homeStack = getOrCreateRootHomeTask(); - if (homeStack != null) { - homeStack.moveToFront(reason); + void moveHomeRootTaskToFront(String reason) { + final Task homeRootTask = getOrCreateRootHomeTask(); + if (homeRootTask != null) { + homeRootTask.moveToFront(reason); } } @@ -1665,7 +1665,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { void moveHomeActivityToTop(String reason) { final ActivityRecord top = getHomeActivity(); if (top == null) { - moveHomeStackToFront(reason); + moveHomeRootTaskToFront(reason); return; } top.moveFocusableActivityToTop(reason); @@ -1697,25 +1697,27 @@ final class TaskDisplayArea extends DisplayArea<Task> { /** * Adjusts the {@param stack} behind the last visible stack in the display if necessary. - * Generally used in conjunction with {@link #moveStackBehindStack}. + * Generally used in conjunction with {@link #moveRootTaskBehindRootTask}. */ // TODO(b/151575894): Remove special stack movement methods. - void moveStackBehindBottomMostVisibleStack(Task stack) { - if (stack.shouldBeVisible(null)) { + void moveRootTaskBehindBottomMostVisibleRootTask(Task rootTask) { + if (rootTask.shouldBeVisible(null)) { // Skip if the stack is already visible return; } // Move the stack to the bottom to not affect the following visibility checks - stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */); + rootTask.getParent().positionChildAt(POSITION_BOTTOM, rootTask, + false /* includingParents */); // Find the next position where the stack should be placed - final boolean isRootTask = stack.isRootTask(); - final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount(); - for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { - final Task s = isRootTask ? getStackAt(stackNdx) - : (Task) stack.getParent().getChildAt(stackNdx); - if (s == stack) { + final boolean isRootTask = rootTask.isRootTask(); + final int numRootTasks = + isRootTask ? getRootTaskCount() : rootTask.getParent().getChildCount(); + for (int rootTaskNdx = 0; rootTaskNdx < numRootTasks; rootTaskNdx++) { + final Task s = isRootTask ? getRootTaskAt(rootTaskNdx) + : (Task) rootTask.getParent().getChildAt(rootTaskNdx); + if (s == rootTask) { continue; } final int winMode = s.getWindowingMode(); @@ -1723,8 +1725,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; if (s.shouldBeVisible(null) && isValidWindowingMode) { // Move the provided stack to behind this stack - final int position = Math.max(0, stackNdx - 1); - stack.getParent().positionChildAt(position, stack, false /*includingParents */); + final int position = Math.max(0, rootTaskNdx - 1); + rootTask.getParent().positionChildAt(position, rootTask, + false /*includingParents */); break; } } @@ -1733,15 +1736,16 @@ final class TaskDisplayArea extends DisplayArea<Task> { /** * Moves the {@param stack} behind the given {@param behindStack} if possible. If * {@param behindStack} is not currently in the display, then then the stack is moved to the - * back. Generally used in conjunction with {@link #moveStackBehindBottomMostVisibleStack}. + * back. Generally used in conjunction with + * {@link #moveRootTaskBehindBottomMostVisibleRootTask}. */ - void moveStackBehindStack(Task stack, Task behindStack) { - if (behindStack == null || behindStack == stack) { + void moveRootTaskBehindRootTask(Task rootTask, Task behindRootTask) { + if (behindRootTask == null || behindRootTask == rootTask) { return; } - final WindowContainer parent = stack.getParent(); - if (parent == null || parent != behindStack.getParent()) { + final WindowContainer parent = rootTask.getParent(); + if (parent == null || parent != behindRootTask.getParent()) { return; } @@ -1749,12 +1753,12 @@ final class TaskDisplayArea extends DisplayArea<Task> { // list, so we need to adjust the insertion index to account for the removed index // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the // position internally - final int stackIndex = parent.mChildren.indexOf(stack); - final int behindStackIndex = parent.mChildren.indexOf(behindStack); + final int stackIndex = parent.mChildren.indexOf(rootTask); + final int behindStackIndex = parent.mChildren.indexOf(behindRootTask); final int insertIndex = stackIndex <= behindStackIndex ? behindStackIndex - 1 : behindStackIndex; final int position = Math.max(0, insertIndex); - parent.positionChildAt(position, stack, false /* includingParents */); + parent.positionChildAt(position, rootTask, false /* includingParents */); } boolean hasPinnedTask() { @@ -1765,20 +1769,20 @@ final class TaskDisplayArea extends DisplayArea<Task> { * @return the stack currently above the {@param stack}. Can be null if the {@param stack} is * already top-most. */ - static Task getStackAbove(Task stack) { - final WindowContainer wc = stack.getParent(); - final int index = wc.mChildren.indexOf(stack) + 1; + static Task getRootTaskAbove(Task rootTask) { + final WindowContainer wc = rootTask.getParent(); + final int index = wc.mChildren.indexOf(rootTask) + 1; return (index < wc.mChildren.size()) ? (Task) wc.mChildren.get(index) : null; } /** Returns true if the stack in the windowing mode is visible. */ - boolean isStackVisible(int windowingMode) { - final Task stack = getTopStackInWindowingMode(windowingMode); - return stack != null && stack.isVisible(); + boolean isRootTaskVisible(int windowingMode) { + final Task rootTask = getTopRootTaskInWindowingMode(windowingMode); + return rootTask != null && rootTask.isVisible(); } - void removeStack(Task stack) { - removeChild(stack); + void removeRootTask(Task rootTask) { + removeChild(rootTask); } int getDisplayId() { @@ -1794,27 +1798,27 @@ final class TaskDisplayArea extends DisplayArea<Task> { * only used by the {@link RecentsAnimation} to determine whether to interrupt and cancel the * current animation when the system state changes. */ - void registerStackOrderChangedListener(OnStackOrderChangedListener listener) { - if (!mStackOrderChangedCallbacks.contains(listener)) { - mStackOrderChangedCallbacks.add(listener); + void registerRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) { + if (!mRootTaskOrderChangedCallbacks.contains(listener)) { + mRootTaskOrderChangedCallbacks.add(listener); } } /** * Removes a previously registered stack order change listener. */ - void unregisterStackOrderChangedListener(OnStackOrderChangedListener listener) { - mStackOrderChangedCallbacks.remove(listener); + void unregisterRootTaskOrderChangedListener(OnRootTaskOrderChangedListener listener) { + mRootTaskOrderChangedCallbacks.remove(listener); } /** - * Notifies of a stack order change + * Notifies of a root task order change * - * @param stack The stack which triggered the order change + * @param rootTask The root task which triggered the order change */ - void onStackOrderChanged(Task stack) { - for (int i = mStackOrderChangedCallbacks.size() - 1; i >= 0; i--) { - mStackOrderChangedCallbacks.get(i).onStackOrderChanged(stack); + void onRootTaskOrderChanged(Task rootTask) { + for (int i = mRootTaskOrderChangedCallbacks.size() - 1; i >= 0; i--) { + mRootTaskOrderChangedCallbacks.get(i).onRootTaskOrderChanged(rootTask); } } @@ -1826,16 +1830,16 @@ final class TaskDisplayArea extends DisplayArea<Task> { /** * Callback for when the order of the stacks in the display changes. */ - interface OnStackOrderChangedListener { - void onStackOrderChanged(Task stack); + interface OnRootTaskOrderChangedListener { + void onRootTaskOrderChanged(Task rootTask); } void ensureActivitiesVisible(ActivityRecord starting, int configChanges, boolean preserveWindows, boolean notifyClients, boolean userLeaving) { mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate(); try { - for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) { - final Task stack = getStackAt(stackNdx); + for (int stackNdx = getRootTaskCount() - 1; stackNdx >= 0; --stackNdx) { + final Task stack = getRootTaskAt(stackNdx); stack.ensureActivitiesVisible(starting, configChanges, preserveWindows, notifyClients, userLeaving); } @@ -1857,7 +1861,7 @@ final class TaskDisplayArea extends DisplayArea<Task> { * @return last reparented stack, or {@code null} if the stacks had to be destroyed. */ Task remove() { - mPreferredTopFocusableStack = null; + mPreferredTopFocusableRootTask = null; // TODO(b/153090332): Allow setting content removal mode per task display area final boolean destroyContentOnRemoval = mDisplayContent.shouldDestroyContentOnRemove(); final TaskDisplayArea toDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); @@ -1869,13 +1873,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { // related WindowContainer will also be removed. So, we set display area as removed after // reparenting stack finished. // Keep the order from bottom to top. - int numStacks = getStackCount(); + int numStacks = getRootTaskCount(); final boolean splitScreenActivated = toDisplayArea.isSplitScreenModeActivated(); final Task rootStack = splitScreenActivated ? toDisplayArea - .getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null; + .getTopRootTaskInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) : null; for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) { - final Task stack = getStackAt(stackNdx); + final Task stack = getRootTaskAt(stackNdx); // Always finish non-standard type stacks and stacks created by a organizer. // TODO: For stacks created by organizer, consider reparenting children tasks if the use // case arises in the future. @@ -1895,8 +1899,8 @@ final class TaskDisplayArea extends DisplayArea<Task> { } // Stacks may be removed from this display. Ensure each stack will be processed // and the loop will end. - stackNdx -= numStacks - getStackCount(); - numStacks = getStackCount(); + stackNdx -= numStacks - getRootTaskCount(); + numStacks = getRootTaskCount(); } if (lastReparentedStack != null && splitScreenActivated) { if (!lastReparentedStack.supportsSplitScreenWindowingMode()) { @@ -1935,18 +1939,19 @@ final class TaskDisplayArea extends DisplayArea<Task> { pw.println(prefix + "TaskDisplayArea " + getName()); final String doublePrefix = prefix + " "; super.dump(pw, doublePrefix, dumpAll); - if (mPreferredTopFocusableStack != null) { - pw.println(doublePrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack); + if (mPreferredTopFocusableRootTask != null) { + pw.println(doublePrefix + "mPreferredTopFocusableRootTask=" + + mPreferredTopFocusableRootTask); } - if (mLastFocusedStack != null) { - pw.println(doublePrefix + "mLastFocusedStack=" + mLastFocusedStack); + if (mLastFocusedRootTask != null) { + pw.println(doublePrefix + "mLastFocusedRootTask=" + mLastFocusedRootTask); } final String triplePrefix = doublePrefix + " "; pw.println(doublePrefix + "Application tokens in top down Z order:"); - for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) { - final Task stack = getChildAt(stackNdx); - pw.println(doublePrefix + "* " + stack); - stack.dump(pw, triplePrefix, dumpAll); + for (int rootTaskNdx = getChildCount() - 1; rootTaskNdx >= 0; --rootTaskNdx) { + final Task rootTask = getChildAt(rootTaskNdx); + pw.println(doublePrefix + "* " + rootTask); + rootTask.dump(pw, triplePrefix, dumpAll); } } } diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 8b2fa52afd22..9d36b84431f3 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -796,9 +796,9 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { @NonNull Rect inOutBounds) { final List<Rect> taskBoundsToCheck = new ArrayList<>(); display.forAllTaskDisplayAreas(taskDisplayArea -> { - int numStacks = taskDisplayArea.getStackCount(); + int numStacks = taskDisplayArea.getRootTaskCount(); for (int sNdx = 0; sNdx < numStacks; ++sNdx) { - final Task task = taskDisplayArea.getStackAt(sNdx); + final Task task = taskDisplayArea.getRootTaskAt(sNdx); if (!task.inFreeformWindowingMode()) { continue; } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 009a7ef9e4f2..e635219bc6e5 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -478,7 +478,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // We want to defer the task appear signal until the task is fully created and attached to // to the hierarchy so that the complete starting configuration is in the task info we send // over to the organizer. - final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode, + final Task task = display.getDefaultTaskDisplayArea().createRootTask(windowingMode, ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(), true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie); task.setDeferTaskAppear(false /* deferTaskAppear */); @@ -688,8 +688,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } ArrayList<RunningTaskInfo> out = new ArrayList<>(); dc.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task task = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task task = taskDisplayArea.getRootTaskAt(sNdx); if (activityTypes != null && !ArrayUtils.contains(activityTypes, task.getActivityType())) { continue; diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java index c976bc207061..855dd7e416b7 100644 --- a/services/core/java/com/android/server/wm/TaskPersister.java +++ b/services/core/java/com/android/server/wm/TaskPersister.java @@ -30,26 +30,28 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; -import java.io.StringWriter; +import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -305,12 +307,9 @@ public class TaskPersister implements PersisterQueue.Listener { continue; } - BufferedReader reader = null; boolean deleteFile = false; - try { - reader = new BufferedReader(new FileReader(taskFile)); - final XmlPullParser in = Xml.newPullParser(); - in.setInput(reader); + try (InputStream is = new FileInputStream(taskFile)) { + final TypedXmlPullParser in = Xml.resolvePullParser(is); int event; while (((event = in.next()) != XmlPullParser.END_DOCUMENT) && @@ -360,7 +359,6 @@ public class TaskPersister implements PersisterQueue.Listener { Slog.e(TAG, "Failing file: " + fileToString(taskFile)); deleteFile = true; } finally { - IoUtils.closeQuietly(reader); if (deleteFile) { if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName()); taskFile.delete(); @@ -513,11 +511,10 @@ public class TaskPersister implements PersisterQueue.Listener { mService = service; } - private StringWriter saveToXml(Task task) throws Exception { + private byte[] saveToXml(Task task) throws Exception { if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task); - final XmlSerializer xmlSerializer = new FastXmlSerializer(); - StringWriter stringWriter = new StringWriter(); - xmlSerializer.setOutput(stringWriter); + final ByteArrayOutputStream os = new ByteArrayOutputStream(); + final TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(os); if (DEBUG) { xmlSerializer.setFeature( @@ -534,13 +531,13 @@ public class TaskPersister implements PersisterQueue.Listener { xmlSerializer.endDocument(); xmlSerializer.flush(); - return stringWriter; + return os.toByteArray(); } @Override public void process() { // Write out one task. - StringWriter stringWriter = null; + byte[] data = null; Task task = mTask; if (DEBUG) Slog.d(TAG, "Writing task=" + task); synchronized (mService.mGlobalLock) { @@ -548,12 +545,12 @@ public class TaskPersister implements PersisterQueue.Listener { // Still there. try { if (DEBUG) Slog.d(TAG, "Saving task=" + task); - stringWriter = saveToXml(task); + data = saveToXml(task); } catch (Exception e) { } } } - if (stringWriter != null) { + if (data != null) { // Write out xml file while not holding mService lock. FileOutputStream file = null; AtomicFile atomicFile = null; @@ -567,8 +564,7 @@ public class TaskPersister implements PersisterQueue.Listener { atomicFile = new AtomicFile(new File(userTasksDir, String.valueOf(task.mTaskId) + TASK_FILENAME_SUFFIX)); file = atomicFile.startWrite(); - file.write(stringWriter.toString().getBytes()); - file.write('\n'); + file.write(data); atomicFile.finishWrite(file); } catch (IOException e) { if (file != null) { diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 7d61c1973a2a..7809cbc2a167 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -502,7 +502,8 @@ class WallpaperController { private void findWallpaperTarget() { mFindResults.reset(); - if (mDisplayContent.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)) { + if (mDisplayContent.getDefaultTaskDisplayArea() + .isRootTaskVisible(WINDOWING_MODE_FREEFORM)) { // In freeform mode we set the wallpaper as its own target, so we don't need an // additional window to make it visible. mFindResults.setUseTopWallpaperAsTarget(true); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a1bb89d26f2f..cf6468d66b57 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1139,18 +1139,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Called when this container or one of its descendants changed its requested orientation, and * wants this container to handle it or pass it to its parent. * - * @param freezeDisplayToken freeze this app window token if display needs to freeze * @param requestingContainer the container which orientation request has changed * @return {@code true} if handled; {@code false} otherwise. */ - boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken, - @Nullable WindowContainer requestingContainer) { + boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) { final WindowContainer parent = getParent(); if (parent == null) { return false; } - return parent.onDescendantOrientationChanged(freezeDisplayToken, - requestingContainer); + return parent.onDescendantOrientationChanged(requestingContainer); } /** @@ -1224,14 +1221,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } /** - * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2 + * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2 * parameters. * * @param orientation the specified orientation. */ void setOrientation(int orientation) { - setOrientation(orientation, null /* freezeDisplayToken */, - null /* ActivityRecord */); + setOrientation(orientation, null /* requestingContainer */); } /** @@ -1240,14 +1236,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * * @param orientation the specified orientation. Needs to be one of {@link * android.content.pm.ActivityInfo.ScreenOrientation}. - * @param freezeDisplayToken uses this token to freeze display if orientation change is not - * done. Display will not be frozen if this is {@code null}, which - * should only happen in tests. * @param requestingContainer the container which orientation request has changed. Mostly used * to ensure it gets correct configuration. */ - void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken, - @Nullable WindowContainer requestingContainer) { + void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) { if (mOrientation == orientation) { return; } @@ -1259,7 +1251,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< // Resolve the requested orientation. onConfigurationChanged(parent.getConfiguration()); } - onDescendantOrientationChanged(freezeDisplayToken, requestingContainer); + onDescendantOrientationChanged(requestingContainer); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java index b0c5dbc6cca3..a5ebf9ac74b9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerConstants.java +++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java @@ -23,7 +23,7 @@ import android.provider.AndroidDeviceConfig; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.wm.utils.DeviceConfigInterface; +import com.android.server.utils.DeviceConfigInterface; import java.io.PrintWriter; import java.util.Objects; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 80b021650c83..d4efa8a7ab91 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -44,10 +44,11 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; @@ -78,7 +79,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManagerGlobal.ADD_OKAY; @@ -199,6 +199,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; +import android.service.attestation.ImpressionToken; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.sysprop.SurfaceFlingerProperties; @@ -256,9 +257,9 @@ import android.view.View; import android.view.WindowContentFrameStats; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; -import android.view.WindowManager.DisplayImePolicy; import android.view.WindowManagerGlobal; import android.view.WindowManagerPolicyConstants.PointerEventListener; import android.window.ClientWindowFrames; @@ -288,8 +289,8 @@ import com.android.server.input.InputManagerService; import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; import com.android.server.power.ShutdownThread; +import com.android.server.utils.DeviceConfigInterface; import com.android.server.utils.PriorityDump; -import com.android.server.wm.utils.DeviceConfigInterface; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -767,6 +768,8 @@ public class WindowManagerService extends IWindowManager.Stub final EmbeddedWindowController mEmbeddedWindowController; final AnrController mAnrController; + private final ImpressionAttestationController mImpressionAttestationController; + @VisibleForTesting final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = @@ -793,8 +796,8 @@ public class WindowManagerService extends IWindowManager.Stub DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM); private final Uri mRenderShadowsInCompositorUri = Settings.Global.getUriFor( DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR); - private final Uri mIgnoreVendorDisplaySettingsUri = Settings.Global.getUriFor( - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS); + private final Uri mDisplaySettingsPathUri = Settings.Global.getUriFor( + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); public SettingsObserver() { super(new Handler()); @@ -819,7 +822,7 @@ public class WindowManagerService extends IWindowManager.Stub UserHandle.USER_ALL); resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mIgnoreVendorDisplaySettingsUri, false, this, + resolver.registerContentObserver(mDisplaySettingsPathUri, false, this, UserHandle.USER_ALL); } @@ -864,8 +867,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } - if (mIgnoreVendorDisplaySettingsUri.equals(uri)) { - updateIgnoreVendorDisplaySettings(); + if (mDisplaySettingsPathUri.equals(uri)) { + updateDisplaySettingsLocation(); return; } @@ -959,12 +962,12 @@ public class WindowManagerService extends IWindowManager.Stub mAtmService.mSizeCompatFreeform = sizeCompatFreeform; } - void updateIgnoreVendorDisplaySettings() { + void updateDisplaySettingsLocation() { final ContentResolver resolver = mContext.getContentResolver(); - final boolean ignoreVendorSettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String filePath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); synchronized (mGlobalLock) { - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorSettings); + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(filePath); mRoot.forAllDisplays(display -> { mDisplayWindowSettings.applySettingsToDisplayLocked(display); display.reconfigureDisplayLocked(); @@ -1327,10 +1330,12 @@ public class WindowManagerService extends IWindowManager.Stub mForceDesktopModeOnExternalDisplays = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; - final boolean ignoreVendorDisplaySettings = Settings.Global.getInt(resolver, - DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS, 0) != 0; + final String displaySettingsPath = Settings.Global.getString(resolver, + DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH); mDisplayWindowSettingsProvider = new DisplayWindowSettingsProvider(); - mDisplayWindowSettingsProvider.setVendorSettingsIgnored(ignoreVendorDisplaySettings); + if (displaySettingsPath != null) { + mDisplayWindowSettingsProvider.setBaseSettingsFilePath(displaySettingsPath); + } mDisplayWindowSettings = new DisplayWindowSettings(this, mDisplayWindowSettingsProvider); IntentFilter filter = new IntentFilter(); @@ -1367,6 +1372,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources( mContext.getResources()); + mImpressionAttestationController = new ImpressionAttestationController(mContext); setGlobalShadowSettings(); mAnrController = new AnrController(this); mStartingSurfaceController = new StartingSurfaceController(this); @@ -1627,7 +1633,7 @@ public class WindowManagerService extends IWindowManager.Stub } final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); - displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + displayPolicy.adjustWindowParamsLw(win, win.mAttrs); win.updateRequestedVisibility(requestedVisibility); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); @@ -2197,7 +2203,7 @@ public class WindowManagerService extends IWindowManager.Stub int flagChanges = 0; int privateFlagChanges = 0; if (attrs != null) { - displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid); + displayPolicy.adjustWindowParamsLw(win, attrs); win.mToken.adjustWindowParams(win, attrs); int disableFlags = (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK; @@ -2918,7 +2924,7 @@ public class WindowManagerService extends IWindowManager.Stub } void getStackBounds(int windowingMode, int activityType, Rect bounds) { - final Task stack = mRoot.getStack(windowingMode, activityType); + final Task stack = mRoot.getRootTask(windowingMode, activityType); if (stack != null) { stack.getBounds(bounds); return; @@ -8428,4 +8434,64 @@ public class WindowManagerService extends IWindowManager.Stub SystemClock.sleep(durationMs); } } + + @Override + public String[] getSupportedImpressionAlgorithms() { + return mImpressionAttestationController.getSupportedImpressionAlgorithms(); + } + + @Override + public boolean verifyImpressionToken(ImpressionToken impressionToken) { + return mImpressionAttestationController.verifyImpressionToken(impressionToken); + } + + ImpressionToken generateImpressionToken(Session session, IWindow window, + Rect boundsInWindow, String hashAlgorithm) { + final SurfaceControl displaySurfaceControl; + final Rect boundsInDisplay = new Rect(boundsInWindow); + synchronized (mGlobalLock) { + final WindowState win = windowForClientLocked(session, window, false); + if (win == null) { + Slog.w(TAG, "Failed to generate impression token. Invalid window"); + return null; + } + + DisplayContent displayContent = win.getDisplayContent(); + if (displayContent == null) { + Slog.w(TAG, "Failed to generate impression token. Window is not on a display"); + return null; + } + + displaySurfaceControl = displayContent.getSurfaceControl(); + mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win, + boundsInWindow, boundsInDisplay); + + if (boundsInDisplay.isEmpty()) { + Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen"); + return null; + } + } + + // A screenshot of the entire display is taken rather than just the window. This is + // because if we take a screenshot of the window, it will not include content that might + // be covering it with the same uid. We want to make sure we include content that's + // covering to ensure we get as close as possible to what the user sees + final int uid = session.mUid; + SurfaceControl.LayerCaptureArgs args = + new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl) + .setUid(uid) + .setSourceCrop(boundsInDisplay) + .build(); + + SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer = + SurfaceControl.captureLayers(args); + if (screenshotHardwareBuffer == null + || screenshotHardwareBuffer.getHardwareBuffer() == null) { + Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot"); + return null; + } + + return mImpressionAttestationController.generateImpressionToken( + screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm); + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index d29534e8080e..1a147b9f73e4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -98,6 +98,8 @@ class ActiveAdmin { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; + private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT = + "password-quality-applies-parent"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = "cross-profile-widget-providers"; @@ -143,6 +145,7 @@ class ActiveAdmin { @NonNull PasswordPolicy mPasswordPolicy = new PasswordPolicy(); + boolean mPasswordPolicyAppliesToParent = true; @DevicePolicyManager.PasswordComplexity int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; @@ -333,6 +336,9 @@ class ActiveAdmin { writeAttributeValueToXml( out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter); } + + writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT, + mPasswordPolicyAppliesToParent); } if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { writeAttributeValueToXml( @@ -626,6 +632,9 @@ class ActiveAdmin { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { mPasswordPolicy.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); + } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) { + mPasswordPolicyAppliesToParent = Boolean.parseBoolean( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); @@ -995,6 +1004,9 @@ class ActiveAdmin { pw.print("minimumPasswordNonLetter="); pw.println(mPasswordPolicy.nonLetter); + pw.print("passwordPolicyAppliesToParent="); + pw.println(mPasswordPolicyAppliesToParent); + pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6ca27b597987..02894b16c079 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -155,10 +155,12 @@ import android.app.admin.SystemUpdateInfo; import android.app.admin.SystemUpdatePolicy; import android.app.admin.UnsafeStateException; import android.app.backup.IBackupManager; +import android.app.compat.CompatChanges; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; +import android.compat.annotation.EnabledSince; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -231,6 +233,7 @@ import android.provider.ContactsInternal; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Telephony; +import android.security.AppUriAuthenticationPolicy; import android.security.IKeyChainAliasCallback; import android.security.IKeyChainService; import android.security.KeyChain; @@ -264,7 +267,6 @@ import android.view.inputmethod.InputMethodInfo; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -527,6 +529,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) private static final long USE_SET_LOCATION_ENABLED = 117835097L; + /** + * Admin apps targeting Android S+ may not use + * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality + * on the {@code DevicePolicyManager} instance obtained by calling + * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}. + * Instead, they should use + * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set + * coarse-grained password requirements device-wide. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) + private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L; + final Context mContext; final Injector mInjector; final IPackageManager mIPackageManager; @@ -539,7 +554,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private final LockSettingsInternal mLockSettingsInternal; private final DeviceAdminServiceController mDeviceAdminServiceController; private final OverlayPackagesProvider mOverlayPackagesProvider; - private final IPlatformCompat mIPlatformCompat; private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl(); private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl(); @@ -972,29 +986,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** - * Checks if the feature is supported and it's safe to execute the given {@code operation}. - * - * <p>Typically called at the beginning of each API method as: - * - * <pre><code> - * - * if (!canExecute(operation, permission)) return; - * - * </code></pre> - * - * @return {@code true} when it's safe to execute, {@code false} when the feature is not - * supported or the caller does not have the given {@code requiredPermission}. + * Checks if it's safe to execute the given {@code operation}. * * @throws UnsafeStateException if it's not safe to execute the operation. */ - boolean canExecute(@DevicePolicyOperation int operation, @NonNull String requiredPermission) { - if (!mHasFeature && !hasCallingPermission(requiredPermission)) { - return false; + private void checkCanExecuteOrThrowUnsafe(@DevicePolicyOperation int operation) { + if (!canExecute(operation)) { + throw mSafetyChecker.newUnsafeStateException(operation); } - if (mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation)) { - return true; - } - throw mSafetyChecker.newUnsafeStateException(operation); + } + + /** + * Returns whether it's safe to execute the given {@code operation}. + */ + boolean canExecute(@DevicePolicyOperation int operation) { + return mSafetyChecker == null || mSafetyChecker.isDevicePolicyOperationSafe(operation); } /** @@ -1147,13 +1153,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(PersistentDataBlockManagerInternal.class); } - LockSettingsInternal getLockSettingsInternal() { - return LocalServices.getService(LockSettingsInternal.class); + AppOpsManager getAppOpsManager() { + return mContext.getSystemService(AppOpsManager.class); } - IPlatformCompat getIPlatformCompat() { - return IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + LockSettingsInternal getLockSettingsInternal() { + return LocalServices.getService(LockSettingsInternal.class); } boolean hasUserSetupCompleted(DevicePolicyData userData) { @@ -1396,6 +1401,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public long systemCurrentTimeMillis() { return System.currentTimeMillis(); } + + public boolean isChangeEnabled(long changeId, String packageName, int userId) { + return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId)); + } } /** @@ -1422,7 +1431,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUsageStatsManagerInternal = Objects.requireNonNull( injector.getUsageStatsManagerInternal()); mIPackageManager = Objects.requireNonNull(injector.getIPackageManager()); - mIPlatformCompat = Objects.requireNonNull(injector.getIPlatformCompat()); mIPermissionManager = Objects.requireNonNull(injector.getIPermissionManager()); mTelephonyManager = Objects.requireNonNull(injector.getTelephonyManager()); @@ -3315,6 +3323,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M; } + private boolean canSetPasswordQualityOnParent(String packageName, int userId) { + return !mInjector.isChangeEnabled( + PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, userId); + } + @Override public void setPasswordQuality(ComponentName who, int quality, boolean parent) { if (!mHasFeature) { @@ -3323,7 +3336,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); validateQualityConstant(quality); - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller)); + + final boolean qualityMayApplyToParent = + canSetPasswordQualityOnParent(who.getPackageName(), caller.getUserId()); + if (!qualityMayApplyToParent) { + Preconditions.checkArgument(!parent, + "Profile Owner may not apply password quality requirements device-wide"); + } + + final int userId = caller.getUserId(); synchronized (getLockObject()) { ActiveAdmin ap = getActiveAdminForCallerLocked( who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent); @@ -3332,6 +3356,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (passwordPolicy.quality != quality) { passwordPolicy.quality = quality; ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE; + ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent; resetInactivePasswordRequirementsIfRPlus(userId, ap); updatePasswordValidityCheckpointLocked(userId, parent); updatePasswordQualityCacheForUserGroup(userId); @@ -3349,13 +3374,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) { - try { - return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY, - packageName, userId); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); - } - return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q; + return mInjector.isChangeEnabled(ADMIN_APP_PASSWORD_COMPLEXITY, packageName, userId); } /** @@ -4063,7 +4082,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId); for (ActiveAdmin admin : admins) { - adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier(); + // Use the password metrics from the admin in one of three cases: + // (1) The admin is of the user we're getting the minimum metrics for. The admin + // always affects the user it's managing. This applies also to the parent + // ActiveAdmin instance: It'd have the same user handle. + // (2) The mPasswordPolicyAppliesToParent field is true: That indicates the + // call to setPasswordQuality was made by an admin that may affect the parent. + if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) { + adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + } } } return PasswordMetrics.merge(adminMetrics); @@ -4155,13 +4183,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id)); ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size()); + int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { adminMetrics.add(admin.mPasswordPolicy.getMinMetrics()); + maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } - //TODO: Take complexity into account, would need to take complexity from all admins - //in the admins list. return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics), - PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty(); + maxRequiredComplexity, false, metrics).isEmpty(); } } @@ -4255,28 +4283,33 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin.mPasswordComplexity = passwordComplexity; // Reset the password policy. admin.mPasswordPolicy = new PasswordPolicy(); + admin.mPasswordPolicyAppliesToParent = true; updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent); updatePasswordQualityCacheForUserGroup(caller.getUserId()); saveSettingsLocked(caller.getUserId()); - //TODO: Log password complexity change if security logging is enabled. }); } + logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(), + caller.getUserId(), calledOnParent, passwordComplexity); } //TODO: Log metrics. } + private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId, + boolean parent, int complexity) { + if (SecurityLog.isLoggingEnabled()) { + final int affectedUserId = parent ? getProfileParentId(userId) : userId; + SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED, + who.getPackageName(), userId, affectedUserId, complexity); + } + } + private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) { ensureLocked(); List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle); int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE; for (ActiveAdmin admin : admins) { - final ComponentName adminComponent = admin.info.getComponent(); - final int adminUser = admin.getUserHandle().getIdentifier(); - // Password complexity is only taken into account from DO/PO - if (isDeviceOwner(adminComponent, adminUser) - || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) { - maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); - } + maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity); } return maxRequiredComplexity; } @@ -4301,6 +4334,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public int getAggregatedPasswordComplexityForUser(int userId) { + if (!mHasFeature) { + return PASSWORD_COMPLEXITY_NONE; + } + + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId)); + + synchronized (getLockObject()) { + return getEffectivePasswordComplexityRequirementLocked(userId); + } + } + + + @Override public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) { if (!mLockPatternUtils.hasSecureLockScreen()) { return 0; @@ -4780,10 +4828,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void lockNow(int flags, boolean parent) { - if (!canExecute(DevicePolicyManager.OPERATION_LOCK_NOW, permission.LOCK_DEVICE)) { - return; - } - final CallerIdentity caller = getCallerIdentity(); final int callingUserId = caller.getUserId(); @@ -4796,6 +4840,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent, android.Manifest.permission.LOCK_DEVICE); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOCK_NOW); final long ident = mInjector.binderClearCallingIdentity(); try { adminComponent = admin == null ? null : admin.info.getComponent(); @@ -4977,7 +5022,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); + || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL) + || isCredentialManagementApp(caller, alias, isUserSelectable)))); final long id = mInjector.binderClearCallingIdentity(); try { @@ -5017,7 +5063,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); + || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL) + || isCredentialManagementApp(caller, alias)))); final long id = Binder.clearCallingIdentity(); try { @@ -5046,6 +5093,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public boolean hasKeyPair(String callerPackage, String alias) { + final CallerIdentity caller = getCallerIdentity(callerPackage); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); + + return mInjector.binderWithCleanCallingIdentity(() -> { + try (KeyChainConnection keyChainConnection = + KeyChain.bindAsUser(mContext, caller.getUserHandle())) { + return keyChainConnection.getService().containsKeyPair(alias); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Querying keypair", e); + } catch (InterruptedException e) { + Log.w(LOG_TAG, "Interrupted while querying keypair", e); + Thread.currentThread().interrupt(); + } + return false; + }); + } + + private boolean canManageCertificates(CallerIdentity caller) { + return isProfileOwner(caller) || isDeviceOwner(caller) + || isCallerDelegate(caller, DELEGATION_CERT_INSTALL); + } + + @Override public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias, String packageName, boolean hasGrant) { Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty"); @@ -5123,9 +5194,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { */ if (hasProfileOwner(caller.getUserId())) { // Make sure that the caller is the profile owner or delegate. - Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate( - caller, DELEGATION_CERT_INSTALL)); + Preconditions.checkCallAuthorization(canManageCertificates(caller)); // Verify that the managed profile is on an organization-owned device and as such // the profile owner can access Device IDs. if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) { @@ -5199,7 +5268,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else { Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); + || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL) + || isCredentialManagementApp(caller, alias)))); } // As the caller will be granted access to the key, ensure no UID was specified, as @@ -5295,7 +5365,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() && (isProfileOwner(caller) || isDeviceOwner(caller))) - || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL))); + || (caller.hasPackage() && (isCallerDelegate(caller, DELEGATION_CERT_INSTALL) + || isCredentialManagementApp(caller, alias)))); final long id = mInjector.binderClearCallingIdentity(); try (final KeyChainConnection keyChainConnection = @@ -5730,6 +5801,70 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + /** + * Check whether a caller application is the credential management app, which can access + * privileged APIs. + * <p> + * This is done by checking that the calling package is authorized to perform the app operation + * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained + * in the aliases specified in the credential management app's authentication policy. The + * key pair to install must not be user selectable. + * + * @param caller the calling identity + * @return {@code true} if the calling process is the credential management app. + */ + private boolean isCredentialManagementApp(CallerIdentity caller, String alias, + boolean isUserSelectable) { + // Should not be user selectable + if (isUserSelectable) { + Log.e(LOG_TAG, "The credential management app is not allowed to install a " + + "user selectable key pair"); + return false; + } + return isCredentialManagementApp(caller, alias); + } + + /** + * Check whether a caller application is the credential mangement app, which can access + * privileged APIs. + * <p> + * This is done by checking that the calling package is authorized to perform the app operation + * {@link android.app.AppOpsManager#OP_MANAGE_CREDENTIALS}. The alias provided must be contained + * in the aliases specified in the credential management app's authentication policy. + * + * @param caller the calling identity + * @return {@code true} if the calling process is the credential management app. + */ + private boolean isCredentialManagementApp(CallerIdentity caller, String alias) { + // Should include alias in authentication policy + try (KeyChainConnection connection = KeyChain.bindAsUser(mContext, + caller.getUserHandle())) { + if (!containsAlias(connection.getService().getCredentialManagementAppPolicy(), alias)) { + return false; + } + } catch (RemoteException | InterruptedException e) { + return false; + } + + AppOpsManager appOpsManager = mInjector.getAppOpsManager(); + return appOpsManager != null + ? appOpsManager.noteOpNoThrow(AppOpsManager.OP_MANAGE_CREDENTIALS, caller.getUid(), + caller.getPackageName(), null, null) == AppOpsManager.MODE_ALLOWED + : false; + } + + private static boolean containsAlias(AppUriAuthenticationPolicy policy, String alias) { + for (Map.Entry<String, Map<Uri, String>> appsToUris : + policy.getAppAndUriMappings().entrySet()) { + for (Map.Entry<Uri, String> urisToAliases : appsToUris.getValue().entrySet()) { + if (urisToAliases.getValue().equals(alias)) { + return true; + } + } + } + return false; + } + @Override public void setCertInstallerPackage(ComponentName who, String installerPackage) throws SecurityException { @@ -7339,6 +7474,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { admin.getPackageName(), userId, "set-device-owner"); Slog.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId); + + if (mInjector.userManagerIsHeadlessSystemUserMode()) { + Slog.i(LOG_TAG, "manageUser: " + admin + " on user " + userId); + + manageUser(admin, admin, caller.getUserId(), null); + } return true; } } @@ -9311,6 +9452,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), "createAndManageUser was called from non-system user"); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); + final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0; final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0 && UserManager.isDeviceInDemoMode(mContext); @@ -9390,29 +9533,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final long id = mInjector.binderClearCallingIdentity(); try { - final String adminPkg = admin.getPackageName(); - try { - // Install the profile owner if not present. - if (!mIPackageManager.isPackageAvailable(adminPkg, userHandle)) { - mIPackageManager.installExistingPackageAsUser(adminPkg, userHandle, - PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, - PackageManager.INSTALL_REASON_POLICY, null); - } - } catch (RemoteException e) { - // Does not happen, same process - } - - // Set admin. - setActiveAdmin(profileOwner, true, userHandle); - final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier()); - setProfileOwner(profileOwner, ownerName, userHandle); - - synchronized (getLockObject()) { - DevicePolicyData policyData = getUserData(userHandle); - policyData.mInitBundle = adminExtras; - policyData.mAdminBroadcastPending = true; - saveSettingsLocked(userHandle); - } + manageUser(admin, profileOwner, userHandle, adminExtras); if ((flags & DevicePolicyManager.SKIP_SETUP_WIZARD) != 0) { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -9433,12 +9554,53 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void manageUser(ComponentName admin, ComponentName profileOwner, + @UserIdInt int userId, PersistableBundle adminExtras) { + // Check for permission + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(canManageUsers(caller)); + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); + mInjector.binderWithCleanCallingIdentity(() -> + manageUserNoCheck(admin, profileOwner, userId, adminExtras)); + } + + private void manageUserNoCheck(ComponentName admin, ComponentName profileOwner, + int user, PersistableBundle adminExtras) { + + final String adminPkg = admin.getPackageName(); + try { + // Install the profile owner if not present. + if (!mIPackageManager.isPackageAvailable(adminPkg, user)) { + mIPackageManager.installExistingPackageAsUser(adminPkg, user, + PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, + PackageManager.INSTALL_REASON_POLICY, null); + } + } catch (RemoteException e) { + // Does not happen, same process + } + + // Set admin. + setActiveAdmin(profileOwner, true, user); + final String ownerName = getProfileOwnerName(Process.myUserHandle().getIdentifier()); + setProfileOwner(profileOwner, ownerName, user); + + synchronized (getLockObject()) { + DevicePolicyData policyData = getUserData(user); + policyData.mInitBundle = adminExtras; + policyData.mAdminBroadcastPending = true; + + saveSettingsLocked(user); + } + } + @Override public boolean removeUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_USER); return mInjector.binderWithCleanCallingIdentity(() -> { String restriction = isManagedProfile(userHandle.getIdentifier()) @@ -9472,6 +9634,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER); synchronized (getLockObject()) { long id = mInjector.binderClearCallingIdentity(); @@ -9496,6 +9659,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND); final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { @@ -9529,6 +9693,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_STOP_USER); final int userId = userHandle.getIdentifier(); if (isManagedProfile(userId)) { @@ -10869,16 +11034,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean isSetSecureSettingLocationModeCheckEnabled(String packageName, int userId) { - long ident = mInjector.binderClearCallingIdentity(); - try { - return mIPlatformCompat.isChangeEnabledByPackageName(USE_SET_LOCATION_ENABLED, - packageName, userId); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); - return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q; - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } + return mInjector.isChangeEnabled(USE_SET_LOCATION_ENABLED, packageName, userId); } @Override @@ -11524,6 +11680,27 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public ComponentName getProfileOwnerAsUser(int userHandle) { return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle); } + + @Override + public boolean isDeviceOrProfileOwnerInCallingUser(String packageName) { + return isDeviceOwnerInCallingUser(packageName) + || isProfileOwnerInCallingUser(packageName); + } + + private boolean isDeviceOwnerInCallingUser(String packageName) { + final ComponentName deviceOwnerInCallingUser = + DevicePolicyManagerService.this.getDeviceOwnerComponent( + /* callingUserOnly= */ true); + return deviceOwnerInCallingUser != null + && packageName.equals(deviceOwnerInCallingUser.getPackageName()); + } + + private boolean isProfileOwnerInCallingUser(String packageName) { + final ComponentName profileOwnerInCallingUser = + getProfileOwnerAsUser(UserHandle.getCallingUserId()); + return profileOwnerInCallingUser != null + && packageName.equals(profileOwnerInCallingUser.getPackageName()); + } } private Intent createShowAdminSupportIntent(ComponentName admin, int userId) { @@ -12085,8 +12262,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) { return CODE_USER_SETUP_COMPLETED; } - } else { - // STOPSHIP Do proper check in split user mode } return CODE_OK; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 10b3265cd081..6525e1126478 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -47,6 +47,7 @@ import android.content.res.Resources.Theme; import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; import android.graphics.GraphicsStatsService; +import android.graphics.Typeface; import android.hardware.display.DisplayManagerInternal; import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; @@ -120,6 +121,7 @@ import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; import com.android.server.gpu.GpuService; +import com.android.server.graphics.fonts.FontManagerService; import com.android.server.hdmi.HdmiControlService; import com.android.server.incident.IncidentCompanionService; import com.android.server.input.InputManagerService; @@ -673,6 +675,11 @@ public final class SystemServer implements Dumpable { SystemServerInitThreadPool tp = SystemServerInitThreadPool.start(); mDumper.addDumpable(tp); + // Load preinstalled system fonts for system server, so that WindowManagerService, etc + // can start using Typeface. Note that fonts are required not only for text rendering, + // but also for some text operations (e.g. TextUtils.makeSafeForPresentation()). + Typeface.loadPreinstalledSystemFontMap(); + // Attach JVMTI agent if this is a debuggable build and the system property is set. if (Build.IS_DEBUGGABLE) { // Property is of the form "library_path=parameters". @@ -1604,6 +1611,10 @@ public final class SystemServer implements Dumpable { } t.traceEnd(); + t.traceBegin("StartFontManagerService"); + mSystemServiceManager.startService(FontManagerService.Lifecycle.class); + t.traceEnd(); + t.traceBegin("StartTextServicesManager"); mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class); t.traceEnd(); diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 7c3b7a67178e..49a41f02c484 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -74,7 +74,14 @@ public class PeopleService extends SystemService { @Override public void onStart() { - publishBinderService(Context.PEOPLE_SERVICE, mService); + onStart(/* isForTesting= */ false); + } + + @VisibleForTesting + protected void onStart(boolean isForTesting) { + if (!isForTesting) { + publishBinderService(Context.PEOPLE_SERVICE, mService); + } publishLocalService(PeopleServiceInternal.class, new LocalService()); } diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index e48b67167cdf..9f895c678dd0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -35,6 +35,8 @@ import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -181,7 +183,7 @@ public class AppOpsUpgradeTest { boolean parse() { try (FileInputStream stream = new FileInputStream(mFile)) { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); int type; while ((type = parser.next()) != XmlPullParser.START_TAG diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 0c2fab83ee66..343b156e443a 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -51,7 +51,6 @@ android_test { "testng", "junit", "platform-compat-test-rules", - ], aidl: { @@ -117,6 +116,7 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", + ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", @@ -133,6 +133,7 @@ java_library { "utils/**/*.java", "utils/**/*.kt", "utils-mockito/**/*.kt", + ":services.core-sources-deviceconfig-interface", ], static_libs: [ "junit", diff --git a/services/tests/servicestests/res/xml/usertypes_test_full.xml b/services/tests/servicestests/res/xml/usertypes_test_full.xml index a281dcaafb3d..099ccbe5b5f6 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_full.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_full.xml @@ -22,4 +22,7 @@ <item res='@*android:color/profile_badge_1' /> </badge-colors> </full-type> + + <change-user-type from="android.old.name" to="android.test.1" whenVersionLeq="1" /> + </user-types> diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml index b6c8fbdcdd20..daa7d7b7341a 100644 --- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml +++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<user-types> +<user-types version="1234"> <profile-type name='android.test.2' max-allowed-per-parent='12' @@ -32,4 +32,7 @@ <default-restrictions no_remove_user='true' no_bluetooth='true' /> </profile-type> <profile-type name='custom.test.1' max-allowed-per-parent='14' /> + + <change-user-type from="android.test.1" to="android.test.2" whenVersionLeq="1233" /> + </user-types> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index b9c2e56a4d90..3f3d5e5106c5 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -17,7 +17,6 @@ package com.android.server.accessibility; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -42,7 +41,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import android.view.Display; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import androidx.test.InstrumentationRegistry; @@ -208,34 +206,6 @@ public class AccessibilityManagerServiceTest extends AndroidTestCase { } @SmallTest - public void testOnMagnificationScaleChanged_MagnificationCapabilitiesAll_showButton() { - // Request showing magnification button if the magnification capability is all mode. - mA11yms.mUserStates.get( - mA11yms.getCurrentUserIdLocked()).setMagnificationCapabilitiesLocked( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); - - mA11yms.onMagnificationScaleChanged(Display.DEFAULT_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - verify(mMockWindowMagnificationMgr).showMagnificationButton(Display.DEFAULT_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - } - - @SmallTest - public void testOnMagnificationScaleChanged_MagnificationCapabilitiesNotAll_NoAction() { - // Do nothing if the magnification capability is not all mode. - mA11yms.mUserStates.get( - mA11yms.getCurrentUserIdLocked()).setMagnificationCapabilitiesLocked( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - mA11yms.onMagnificationScaleChanged(Display.DEFAULT_DISPLAY, - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); - - verify(mMockWindowMagnificationMgr, never()).showMagnificationButton(anyInt(), - anyInt()); - } - - @SmallTest public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() { final AccessibilityUserState userState = mA11yms.mUserStates.get( mA11yms.getCurrentUserIdLocked()); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index e43a002806ee..c038a0f33904 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -54,7 +54,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accessibility.EventStreamTransformation; import com.android.server.accessibility.magnification.FullScreenMagnificationController.MagnificationRequestObserver; -import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener; import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import com.android.server.wm.WindowManagerInternal; @@ -125,7 +124,7 @@ public class FullScreenMagnificationGestureHandlerTest { private Context mContext; FullScreenMagnificationController mFullScreenMagnificationController; @Mock - ScaleChangedListener mMockScaleChangedListener; + MagnificationGestureHandler.ScaleChangedListener mMockScaleChangedListener; @Mock MagnificationRequestObserver mMagnificationRequestObserver; @Mock @@ -181,8 +180,8 @@ public class FullScreenMagnificationGestureHandlerTest { boolean detectShortcutTrigger) { FullScreenMagnificationGestureHandler h = new FullScreenMagnificationGestureHandler( mContext, mFullScreenMagnificationController, mMockScaleChangedListener, - detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController, - DISPLAY_0); + detectTripleTap, detectShortcutTrigger, + mWindowMagnificationPromptController, DISPLAY_0); mHandler = new TestHandler(h.mDetectingState, mClock) { @Override protected String messageToString(Message m) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index aed590b8b7e0..cba618bf94b8 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -106,8 +107,8 @@ public class MagnificationControllerTest { mock(WindowMagnificationManager.Callback.class))); mMockConnection = new MockWindowMagnificationConnection(true); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mMagnificationController = new MagnificationController(mService, new Object(), mContext, - mScreenMagnificationController, mWindowMagnificationManager); + mMagnificationController = spy(new MagnificationController(mService, new Object(), mContext, + mScreenMagnificationController, mWindowMagnificationManager)); } @After @@ -277,8 +278,32 @@ public class MagnificationControllerTest { verify(mWindowMagnificationManager).setScale(eq(TEST_DISPLAY), eq(newScale)); verify(mWindowMagnificationManager).persistScale(eq(TEST_DISPLAY)); - verify(mService).onMagnificationScaleChanged(eq(TEST_DISPLAY), - eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); + verify(mMagnificationController).onMagnificationScaleChanged(eq(TEST_DISPLAY), + eq(MODE_WINDOW)); + } + + @Test + public void onMagnificationScaleChanged_capabilitiesAllMode_showMagnificationButton() + throws RemoteException { + mMagnificationController.setMagnificationCapabilities( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL); + + mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW); + + verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY), + eq(MODE_WINDOW)); + } + + @Test + public void onMagnificationScaleChanged_capabilitiesNotAllMode_notShowMagnificationButton() + throws RemoteException { + mMagnificationController.setMagnificationCapabilities( + Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN); + + mMagnificationController.onMagnificationScaleChanged(TEST_DISPLAY, MODE_WINDOW); + + verify(mWindowMagnificationManager, never()).showMagnificationButton(eq(TEST_DISPLAY), + eq(MODE_WINDOW)); } private void setMagnificationEnabled(int mode) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index 41b6e987f819..9f930da4ee29 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -32,7 +32,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.server.accessibility.EventStreamTransformation; -import com.android.server.accessibility.magnification.MagnificationGestureHandler.ScaleChangedListener; import com.android.server.accessibility.utils.TouchEventGenerator; import org.junit.After; @@ -74,7 +73,8 @@ public class WindowMagnificationGestureHandlerTest { mock(WindowMagnificationManager.Callback.class)); mMockConnection = new MockWindowMagnificationConnection(); mWindowMagnificationGestureHandler = new WindowMagnificationGestureHandler( - mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), + mContext, mWindowMagnificationManager, mock( + MagnificationGestureHandler.ScaleChangedListener.class), /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 904e93b7d8cf..e2b48d439a47 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -127,6 +127,8 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi // Used as an override when set to nonzero. private long mCurrentTimeMillis = 0; + private final Map<Long, Pair<String, Integer>> mEnabledChanges = new ArrayMap<>(); + public MockInjector(MockSystemServices services, DpmMockContext context) { super(context); this.services = services; @@ -487,5 +489,33 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi public long systemCurrentTimeMillis() { return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis(); } + + public void setChangeEnabledForPackage( + long changeId, boolean enabled, String packageName, int userId) { + if (enabled) { + mEnabledChanges.put(changeId, Pair.create(packageName, userId)); + } else { + mEnabledChanges.remove(changeId); + } + } + + public void clearEnabledChanges() { + mEnabledChanges.clear(); + } + + @Override + public boolean isChangeEnabled(long changeId, String packageName, int userId) { + Pair<String, Integer> packageAndUser = mEnabledChanges.get(changeId); + if (packageAndUser == null) { + return false; + } + + if (!packageAndUser.first.equals(packageName) + || !packageAndUser.second.equals(userId)) { + return false; + } + + return true; + } } } 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 c0c82d53b4f0..e2c5e97b8896 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4252,10 +4252,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.packageName = admin1.getPackageName(); mContext.applicationInfo = new ApplicationInfo(); - when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject())) - .thenReturn(Color.WHITE); - when(mContext.resources.getColor(eq(R.color.notification_material_background_color), - anyObject())).thenReturn(Color.WHITE); + when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the // feature is disabled because there are non-affiliated secondary users. @@ -4301,10 +4298,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { setupDeviceOwner(); mContext.packageName = admin1.getPackageName(); mContext.applicationInfo = new ApplicationInfo(); - when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject())) - .thenReturn(Color.WHITE); - when(mContext.resources.getColor(eq(R.color.notification_material_background_color), - anyObject())).thenReturn(Color.WHITE); + when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE); // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the // feature is disabled because there are non-affiliated secondary users. @@ -5090,11 +5084,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { doReturn(true).when(getServices().lockPatternUtils) .isSeparateProfileChallengeEnabled(managedProfileUserId); - dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC); - parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC); + dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); + parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPassword("1234".getBytes())); + .thenReturn(computeForPassword("184342".getBytes())); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) @@ -5107,6 +5101,68 @@ public class DevicePolicyManagerTest extends DpmTestBase { managedProfileUserId)).isFalse(); } + @Test + public void testCanSetPasswordRequirementOnParentPreS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + dpms.mMockInjector.setChangeEnabledForPackage(165573442L, false, + admin1.getPackageName(), managedProfileUserId); + + parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(admin1)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + } + + @Test + public void testCannotSetPasswordRequirementOnParent() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1); + dpms.mMockInjector.setChangeEnabledForPackage(165573442L, true, + admin1.getPackageName(), managedProfileUserId); + + try { + assertExpectException(IllegalArgumentException.class, null, () -> + parentDpm.setPasswordQuality( + admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)); + } finally { + dpms.mMockInjector.clearEnabledChanges(); + } + } + + @Test + public void testPasswordQualityAppliesToParentPreS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE)) + .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0)); + + dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(null)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + } + + @Test + public void testPasswordQualityDoesNotApplyToParentPostS() throws Exception { + final int managedProfileUserId = CALLER_USER_HANDLE; + final int managedProfileAdminUid = + UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID); + mContext.binder.callingUid = managedProfileAdminUid; + addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R); + + dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX); + assertThat(parentDpm.getPasswordQuality(admin1)) + .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + } + private void setActivePasswordState(PasswordMetrics passwordMetrics) throws Exception { final int userHandle = UserHandle.getUserId(mContext.binder.callingUid); @@ -7014,20 +7070,32 @@ public class DevicePolicyManagerTest extends DpmTestBase { * @param adminUid uid of the admin package. * @param copyFromAdmin package information for {@code admin} will be built based on this * component's information. + * @param appTargetSdk admin's target SDK level */ private void addManagedProfile( - ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { + ComponentName admin, int adminUid, ComponentName copyFromAdmin, int appTargetSdk) + throws Exception { final int userId = UserHandle.getUserId(adminUid); getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED, UserHandle.USER_SYSTEM); mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS); - setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin); + setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null, + appTargetSdk, copyFromAdmin); dpm.setActiveAdmin(admin, false, userId); assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue(); mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS); } /** + * Same as {@code addManagedProfile} above, except using development API level as the API + * level of the admin. + */ + private void addManagedProfile( + ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception { + addManagedProfile(admin, adminUid, copyFromAdmin, VERSION_CODES.CUR_DEVELOPMENT); + } + + /** * Convert String[] to StringParceledListSlice. */ private static StringParceledListSlice asSlice(String[] s) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java index 3aa5a80d814f..d58d71fb44c3 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java @@ -23,6 +23,8 @@ import static org.mockito.Mockito.when; import android.app.admin.FactoryResetProtectionPolicy; import android.os.Parcel; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -98,7 +100,7 @@ public class FactoryResetProtectionPolicyTest { throws Exception { ByteArrayOutputStream outStream = serialize(policy); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); @@ -109,7 +111,7 @@ public class FactoryResetProtectionPolicyTest { throws Exception { ByteArrayOutputStream outStream = serialize(policy); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = mock(XmlPullParser.class); + TypedXmlPullParser parser = mock(TypedXmlPullParser.class); when(parser.next()).thenThrow(XmlPullParserException.class); parser.setInput(new InputStreamReader(inStream)); @@ -120,7 +122,7 @@ public class FactoryResetProtectionPolicyTest { private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy) throws IOException { ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java index 0a9aad771ff0..1308a3e74881 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java @@ -29,6 +29,8 @@ import static org.junit.Assert.fail; import android.app.admin.FreezePeriod; import android.app.admin.SystemUpdatePolicy; import android.os.Parcel; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -471,7 +473,7 @@ public final class SystemUpdatePolicyTest { // Test XML serialization ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); outXml.startTag(null, "ota"); @@ -481,7 +483,7 @@ public final class SystemUpdatePolicyTest { outXml.flush(); ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG); checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 325ba118c711..cb5ca04c1fde 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -16,49 +16,97 @@ package com.android.server.display; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; +import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; + +import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import android.annotation.NonNull; +import android.content.ContentResolver; import android.content.Context; +import android.content.ContextWrapper; +import android.database.ContentObserver; +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.os.Handler; import android.os.Looper; +import android.provider.DeviceConfig; +import android.provider.Settings; +import android.test.mock.MockContentResolver; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; -import androidx.test.InstrumentationRegistry; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.Preconditions; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + @SmallTest @RunWith(AndroidJUnit4.class) public class DisplayModeDirectorTest { // The tolerance within which we consider something approximately equals. + private static final String TAG = "DisplayModeDirectorTest"; + private static final boolean DEBUG = false; private static final float FLOAT_TOLERANCE = 0.01f; private Context mContext; + private FakesInjector mInjector; + private Handler mHandler; + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); + final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext); + when(mContext.getContentResolver()).thenReturn(resolver); + mInjector = new FakesInjector(); + mHandler = new Handler(Looper.getMainLooper()); } private DisplayModeDirector createDirectorFromRefreshRateArray( float[] refreshRates, int baseModeId) { DisplayModeDirector director = - new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper())); + new DisplayModeDirector(mContext, mHandler, mInjector); int displayId = 0; Display.Mode[] modes = new Display.Mode[refreshRates.length]; for (int i = 0; i < refreshRates.length; i++) { @@ -159,9 +207,9 @@ public class DisplayModeDirectorTest { } @Test - public void testBrightnessHasLowerPriorityThanUser() { - assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); - assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE); + public void testFlickerHasLowerPriorityThanUser() { + assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE); + assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE); int displayId = 0; DisplayModeDirector director = createDirectorFromFpsRange(60, 90); @@ -169,7 +217,7 @@ public class DisplayModeDirectorTest { SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -177,7 +225,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); @@ -185,7 +233,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90); @@ -193,7 +241,7 @@ public class DisplayModeDirectorTest { votes.clear(); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60)); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90)); director.injectVotesByDisplay(votesByDisplay); desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -202,10 +250,10 @@ public class DisplayModeDirectorTest { @Test public void testAppRequestRefreshRateRange() { - // Confirm that the app request range doesn't include low brightness or min refresh rate - // settings, but does include everything else. + // Confirm that the app request range doesn't include flicker or min refresh rate settings, + // but does include everything else. assertTrue( - Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); + PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF); assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE @@ -216,7 +264,7 @@ public class DisplayModeDirectorTest { SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60)); director.injectVotesByDisplay(votesByDisplay); DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60); @@ -310,7 +358,7 @@ public class DisplayModeDirectorTest { SparseArray<Vote> votes = new SparseArray<>(); SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(displayId, votes); - votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60)); + votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60)); votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90)); votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90)); votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60)); @@ -398,4 +446,343 @@ public class DisplayModeDirectorTest { DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId); assertThat(desiredSpecs.allowGroupSwitching).isTrue(); } + + @Test + public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + SensorManager sensorManager = createMockSensorManager(createLightSensor()); + + final int initialRefreshRate = 60; + mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate); + director.start(sensorManager); + assertThat(director.getBrightnessObserver().getRefreshRateInLowZone()) + .isEqualTo(initialRefreshRate); + + final int updatedRefreshRate = 90; + mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getBrightnessObserver().getRefreshRateInLowZone()) + .isEqualTo(updatedRefreshRate); + } + + @Test + public void testBrightnessObserverThresholdsInZone() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0); + SensorManager sensorManager = createMockSensorManager(createLightSensor()); + + final int[] initialDisplayThresholds = { 10 }; + final int[] initialAmbientThresholds = { 20 }; + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setLowDisplayBrightnessThresholds(initialDisplayThresholds); + config.setLowAmbientBrightnessThresholds(initialAmbientThresholds); + director.start(sensorManager); + + assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds()) + .isEqualTo(initialDisplayThresholds); + assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds()) + .isEqualTo(initialAmbientThresholds); + + final int[] updatedDisplayThresholds = { 9, 14 }; + final int[] updatedAmbientThresholds = { -1, 19 }; + config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds); + config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds); + // Need to wait for the property change to propagate to the main thread. + waitForIdleSync(); + assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds()) + .isEqualTo(updatedDisplayThresholds); + assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds()) + .isEqualTo(updatedAmbientThresholds); + } + + @Test + public void testLockFpsForLowZone() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(true); + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setRefreshRateInLowZone(90); + config.setLowDisplayBrightnessThresholds(new int[] { 10 }); + config.setLowAmbientBrightnessThresholds(new int[] { 20 }); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + setBrightness(10); + // Sensor reads 20 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertVoteForRefreshRateLocked(vote, 90 /*fps*/); + + setBrightness(125); + // Sensor reads 1000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertThat(vote).isNull(); + } + + @Test + public void testLockFpsForHighZone() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + setPeakRefreshRate(90 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(90); + director.getBrightnessObserver().setDefaultDisplayState(true); + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setRefreshRateInHighZone(60); + config.setHighDisplayBrightnessThresholds(new int[] { 255 }); + config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + + director.start(sensorManager); + + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + setBrightness(100); + // Sensor reads 2000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000)); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertThat(vote).isNull(); + + setBrightness(255); + // Sensor reads 9000 lux, + listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000)); + + vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER); + assertVoteForRefreshRateLocked(vote, 60 /*fps*/); + } + + private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) { + assertThat(vote).isNotNull(); + final DisplayModeDirector.RefreshRateRange expectedRange = + new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate); + assertThat(vote.refreshRateRange).isEqualTo(expectedRange); + } + + private static class FakeDeviceConfig extends FakeDeviceConfigInterface { + @Override + public String getProperty(String namespace, String name) { + Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)); + return super.getProperty(namespace, name); + } + + @Override + public void addOnPropertiesChangedListener( + String namespace, + Executor executor, + DeviceConfig.OnPropertiesChangedListener listener) { + Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace)); + super.addOnPropertiesChangedListener(namespace, executor, listener); + } + + void setRefreshRateInLowZone(int fps) { + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE, + String.valueOf(fps)); + } + + void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) { + String thresholds = toPropertyValue(brightnessThresholds); + + if (DEBUG) { + Slog.e(TAG, "Brightness Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setLowAmbientBrightnessThresholds(int[] ambientThresholds) { + String thresholds = toPropertyValue(ambientThresholds); + + if (DEBUG) { + Slog.e(TAG, "Ambient Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setRefreshRateInHighZone(int fps) { + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE, + String.valueOf(fps)); + } + + void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) { + String thresholds = toPropertyValue(brightnessThresholds); + + if (DEBUG) { + Slog.e(TAG, "Brightness Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + void setHighAmbientBrightnessThresholds(int[] ambientThresholds) { + String thresholds = toPropertyValue(ambientThresholds); + + if (DEBUG) { + Slog.e(TAG, "Ambient Thresholds = " + thresholds); + } + + putPropertyAndNotify( + DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS, + thresholds); + } + + @NonNull + private static String toPropertyValue(@NonNull int[] intArray) { + return Arrays.stream(intArray) + .mapToObj(Integer::toString) + .collect(Collectors.joining(",")); + } + } + + private void setBrightness(int brightness) { + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, + brightness); + mInjector.notifyBrightnessChanged(); + waitForIdleSync(); + } + + private void setPeakRefreshRate(float fps) { + Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, + fps); + mInjector.notifyPeakRefreshRateChanged(); + waitForIdleSync(); + } + + private static SensorManager createMockSensorManager(Sensor... sensors) { + SensorManager sensorManager = Mockito.mock(SensorManager.class); + when(sensorManager.getSensorList(anyInt())).then((invocation) -> { + List<Sensor> requestedSensors = new ArrayList<>(); + int type = invocation.getArgument(0); + for (Sensor sensor : sensors) { + if (sensor.getType() == type || type == Sensor.TYPE_ALL) { + requestedSensors.add(sensor); + } + } + return requestedSensors; + }); + + when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> { + int type = invocation.getArgument(0); + for (Sensor sensor : sensors) { + if (sensor.getType() == type) { + return sensor; + } + } + return null; + }); + return sensorManager; + } + + private static Sensor createLightSensor() { + try { + return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT); + } catch (Exception e) { + // There's nothing we can do if this fails, just throw a RuntimeException so that we + // don't have to mark every function that might call this as throwing Exception + throw new RuntimeException("Failed to create a light sensor", e); + } + } + + private void waitForIdleSync() { + mHandler.runWithScissors(() -> { }, 500 /*timeout*/); + } + + static class FakesInjector implements DisplayModeDirector.Injector { + private final FakeDeviceConfig mDeviceConfig; + private ContentObserver mBrightnessObserver; + private ContentObserver mPeakRefreshRateObserver; + + FakesInjector() { + mDeviceConfig = new FakeDeviceConfig(); + } + + @NonNull + public FakeDeviceConfig getDeviceConfig() { + return mDeviceConfig; + } + + @Override + public void registerBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + if (mBrightnessObserver != null) { + throw new IllegalStateException("Tried to register a second brightness observer"); + } + mBrightnessObserver = observer; + } + + @Override + public void unregisterBrightnessObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mBrightnessObserver = null; + } + + void notifyBrightnessChanged() { + if (mBrightnessObserver != null) { + mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI); + } + } + + @Override + public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mPeakRefreshRateObserver = observer; + } + + void notifyPeakRefreshRateChanged() { + if (mPeakRefreshRateObserver != null) { + mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, + PEAK_REFRESH_RATE_URI); + } + } + + @Override + public boolean isDeviceInteractive(@NonNull Context context) { + return true; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java index de9a5e4f0fe7..d10e075c5136 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java @@ -18,17 +18,13 @@ package com.android.server.hdmi; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; -import android.annotation.NonNull; import android.content.Context; import android.hardware.hdmi.HdmiControlManager; -import android.os.Looper; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; @@ -42,21 +38,15 @@ import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - @SmallTest @Presubmit @RunWith(JUnit4.class) public final class HdmiCecConfigTest { private static final String TAG = "HdmiCecConfigTest"; - private static final int TIMEOUT_CONTENT_CHANGE_SEC = 4; - private Context mContext; @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter; - @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener; @Before public void setUp() throws Exception { @@ -1029,105 +1019,4 @@ public final class HdmiCecConfigTest { HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED)); } - - @Test - public void registerChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.setIntValue( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); - verify(mSettingChangeListener).onChange( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); - } - - @Test - public void removeChangeListener_SharedPref_BasicSanity() { - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"system_audio_mode_muting\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.removeChangeListener( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - mSettingChangeListener); - hdmiCecConfig.setIntValue( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING, - HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED); - verify(mSettingChangeListener, never()).onChange( - HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING); - } - - /** - * Externally modified Global Settings still need to be supported. This test verifies that - * setting change notification is being forwarded to listeners registered via HdmiCecConfig. - */ - @Test - public void globalSettingObserver_BasicSanity() throws Exception { - CountDownLatch notifyLatch = new CountDownLatch(1); - // Get current value of the setting in the system. - String val = Global.getString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED); - HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings( - mContext, mStorageAdapter, - "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<cec-settings>" - + " <setting name=\"hdmi_cec_enabled\"" - + " value-type=\"int\"" - + " user-configurable=\"true\">" - + " <allowed-values>" - + " <value int-value=\"0\" />" - + " <value int-value=\"1\" />" - + " </allowed-values>" - + " <default-value int-value=\"1\" />" - + " </setting>" - + "</cec-settings>", null); - hdmiCecConfig.registerGlobalSettingsObserver(Looper.getMainLooper()); - HdmiCecConfig.SettingChangeListener latchUpdateListener = - new HdmiCecConfig.SettingChangeListener() { - @Override - public void onChange(@NonNull @HdmiControlManager.CecSettingName String setting) { - notifyLatch.countDown(); - assertThat(setting).isEqualTo(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED); - } - }; - hdmiCecConfig.registerChangeListener( - HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED, - latchUpdateListener); - // Flip the value of the setting. - Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, - ((val == null || val.equals("1")) ? "0" : "1")); - if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) { - fail("Timed out waiting for the notify callback"); - } - hdmiCecConfig.unregisterGlobalSettingsObserver(); - // Restore the previous value of the setting in the system. - Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED, val); - } } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index 9ef755791c80..20f9b703e29d 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -27,6 +27,8 @@ import static org.junit.Assert.fail; import android.content.om.OverlayInfo; import android.text.TextUtils; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -403,7 +405,7 @@ public class OverlayManagerSettingsTests { private int countXmlTags(String xml, String tagToLookFor) throws Exception { int count = 0; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new StringReader(xml)); int event = parser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { @@ -418,7 +420,7 @@ public class OverlayManagerSettingsTests { private int countXmlAttributesWhere(String xml, String tag, String attr, String value) throws Exception { int count = 0; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new StringReader(xml)); int event = parser.getEventType(); while (event != XmlPullParser.END_DOCUMENT) { diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java index 9213e1fe5a25..a112b145fdc9 100644 --- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java @@ -69,7 +69,7 @@ public final class PeopleServiceTest { when(mCallback.asBinder()).thenReturn(new Binder()); PeopleService service = new PeopleService(mContext); - service.onStart(); + service.onStart(/* isForTesting= */ true); mServiceInternal = LocalServices.getService(PeopleServiceInternal.class); mLocalService = (PeopleService.LocalService) mServiceInternal; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index bbb83b6b46cf..11d00f0fd406 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -29,6 +29,8 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java index 89c3d5006347..90658055ad6f 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java @@ -25,6 +25,8 @@ import android.content.Context; import android.content.pm.PackageParser; import android.content.pm.Signature; import android.util.TypedXmlPullParser; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 194ae055e01f..23fcf701a8bb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -98,6 +98,8 @@ import android.os.UserHandle; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.frameworks.servicestests.R; @@ -8695,7 +8697,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Write ShareTargets to Xml ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final XmlSerializer outXml = new FastXmlSerializer(); + final TypedXmlSerializer outXml = Xml.newFastSerializer(); outXml.setOutput(outStream, StandardCharsets.UTF_8.name()); outXml.startDocument(null, true); for (int i = 0; i < expectedValues.size(); i++) { @@ -8706,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // Read ShareTargets from Xml ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new InputStreamReader(inStream)); List<ShareTargetInfo> shareTargets = new ArrayList<>(); int type; diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java index 4fac9dc391e3..dfc25e0c7cb6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java @@ -21,6 +21,7 @@ import static android.content.pm.UserInfo.FLAG_DISABLED; import static android.content.pm.UserInfo.FLAG_EPHEMERAL; import static android.content.pm.UserInfo.FLAG_FULL; import static android.content.pm.UserInfo.FLAG_GUEST; +import static android.content.pm.UserInfo.FLAG_INITIALIZED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PROFILE; import static android.content.pm.UserInfo.FLAG_RESTRICTED; @@ -44,6 +45,7 @@ import android.content.pm.UserInfo.UserInfoFlag; import android.os.Looper; import android.os.Parcel; import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; import androidx.test.InstrumentationRegistry; @@ -206,6 +208,8 @@ public class UserManagerServiceUserInfoTest { @Test public void testUpgradeIfNecessaryLP_9() { final int versionToTest = 9; + // do not trigger a user type upgrade + final int userTypeVersion = UserTypeFactory.getUserTypeVersion(); mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null)); mUserManagerService.putUserInfo(createUser(101, @@ -216,7 +220,7 @@ public class UserManagerServiceUserInfoTest { mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null)); mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null)); - mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1); + mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion); assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED)); assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0); @@ -278,4 +282,86 @@ public class UserManagerServiceUserInfoTest { two.convertedFromPreCreated); } } + + /** Tests upgrading profile types */ + @Test + public void testUpgradeProfileType_updateTypeAndFlags() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, + oldUserType.getDefaultUserInfoFlags() | FLAG_INITIALIZED, oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.isUserOfType(userId, newUserTypeName)); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_PROFILE) != 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_MANAGED_PROFILE) == 0); + assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_INITIALIZED) != 0); + } + + @Test + public void testUpgradeProfileType_updateRestrictions() { + final int userId = 42; + final String newUserTypeName = "new.user.type"; + final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED; + + UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder() + .setName(oldUserTypeName) + .setBaseType(FLAG_PROFILE) + .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25); + UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails(); + + UserInfo userInfo = createUser(userId, oldUserType.getDefaultUserInfoFlags(), + oldUserTypeName); + mUserManagerService.putUserInfo(userInfo); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_CAMERA, true, userId); + mUserManagerService.setUserRestriction(UserManager.DISALLOW_PRINTING, true, userId); + + UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder() + .setName(newUserTypeName) + .setBaseType(FLAG_PROFILE) + .setMaxAllowedPerParent(32) + .setIconBadge(401) + .setBadgeColors(402, 403, 404) + .setBadgeLabels(23, 24, 25) + .setDefaultRestrictions( + UserManagerServiceUserTypeTest.makeRestrictionsBundle( + UserManager.DISALLOW_WALLPAPER)); + UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails(); + + mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType); + + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_PRINTING)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_CAMERA)); + assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean( + UserManager.DISALLOW_WALLPAPER)); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java index 8e74c903534f..ee30f68de7a0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java @@ -51,6 +51,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}. * @@ -358,13 +360,56 @@ public class UserManagerServiceUserTypeTest { () -> UserTypeFactory.customizeBuilders(builders, parser)); } + @Test + public void testUserTypeFactoryVersion_versionMissing() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray); + assertEquals(0, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryVersion_versionPresent() { + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser)); + } + + @Test + public void testUserTypeFactoryUpgrades_validUpgrades() { + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put("name", getMinimalBuilder()); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile); + List<UserTypeFactory.UserTypeUpgrade> upgrades = UserTypeFactory.parseUserUpgrades(builders, + parser); + + assertFalse(upgrades.isEmpty()); + UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0); + assertEquals("android.test.1", upgrade.getFromType()); + assertEquals("android.test.2", upgrade.getToType()); + assertEquals(1233, upgrade.getUpToVersion()); + } + + @Test + public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() { + final String userTypeFull = "android.test.1"; + final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>(); + builders.put(userTypeFull, new UserTypeDetails.Builder() + .setName(userTypeFull) + .setBaseType(FLAG_FULL)); + + final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full); + + // parser is illegal because the "to" upgrade type is not a profile, but a full user + assertThrows(IllegalArgumentException.class, + () -> UserTypeFactory.parseUserUpgrades(builders, parser)); + } + /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */ private UserTypeDetails.Builder getMinimalBuilder() { return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL); } /** Creates a Bundle of the given String restrictions, each set to true. */ - private Bundle makeRestrictionsBundle(String ... restrictions) { + public static Bundle makeRestrictionsBundle(String ... restrictions) { final Bundle bundle = new Bundle(); for (String restriction : restrictions) { bundle.putBoolean(restriction, true); diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java index 0f9bf2f6cd86..8bccce102887 100644 --- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java +++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java @@ -367,7 +367,7 @@ public class SearchablesTest extends AndroidTestCase { * for you if needed; if you already have this information around, it can * be much more efficient to supply it here. * - * @return Returns an XmlPullParser allowing you to parse out the XML + * @return Returns an TypedXmlPullParser allowing you to parse out the XML * data. Returns null if the xml resource could not be found for any * reason. */ diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java index 1d62e01c068d..7ac493828610 100644 --- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java @@ -21,6 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.usage.CacheQuotaHint; import android.test.AndroidTestCase; import android.util.Pair; +import android.util.TypedXmlSerializer; +import android.util.Xml; import com.android.internal.util.FastXmlSerializer; @@ -37,12 +39,12 @@ import java.util.List; @RunWith(JUnit4.class) public class CacheQuotaStrategyTest extends AndroidTestCase { StringWriter mWriter; - FastXmlSerializer mOut; + TypedXmlSerializer mOut; @Before public void setUp() throws Exception { mWriter = new StringWriter(); - mOut = new FastXmlSerializer(); + mOut = Xml.newFastSerializer(); mOut.setOutput(mWriter); } diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index c23fb8028224..21396fd0516e 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -525,6 +525,7 @@ public class TimeDetectorStrategyImplTest { @Test public void testSuggestNetworkTime_autoTimeEnabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(true); NetworkTimeSuggestion timeSuggestion = @@ -541,6 +542,7 @@ public class TimeDetectorStrategyImplTest { @Test public void testSuggestNetworkTime_autoTimeDisabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(false); NetworkTimeSuggestion timeSuggestion = @@ -552,10 +554,26 @@ public class TimeDetectorStrategyImplTest { } @Test - public void testSuggestNetworkTime_telephonySuggestionsBeatNetworkSuggestions() { + public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoOriginPriorities(ORIGIN_NETWORK) .pokeAutoTimeDetectionEnabled(true); + Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); + NetworkTimeSuggestion timeSuggestion = mScript + .generateNetworkTimeSuggestion(suggestedTime); + + mScript.simulateNetworkTimeSuggestion(timeSuggestion) + .verifySystemClockWasNotSetAndResetCallTracking() + .assertLatestNetworkSuggestion(null); + } + + @Test + public void highPrioritySuggestionsShouldBeatLowerPrioritySuggestions() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); + // Three obviously different times that could not be mistaken for each other. Instant networkTime1 = ARBITRARY_TEST_TIME; Instant networkTime2 = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -576,7 +594,7 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, null) .assertLatestNetworkSuggestion(networkTimeSuggestion1); assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); + assertNull("No telephony suggestions were made:", mScript.peekBestTelephonySuggestion()); // Simulate a little time passing. mScript.simulateTimePassing(smallTimeIncrementMillis) @@ -631,7 +649,9 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); + assertNull( + "Telephony suggestion should be expired:", + mScript.peekBestTelephonySuggestion()); // Toggle auto-time off and on to force the detection logic to run. mScript.simulateAutoTimeDetectionToggle() @@ -646,27 +666,16 @@ public class TimeDetectorStrategyImplTest { mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestTelephonySuggestion()); - } - - @Test - public void networkTimeSuggestion_ignoredWhenReferencedTimeIsInThePast() { - mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); - - Instant suggestedTime = TIME_LOWER_BOUND.minus(Duration.ofDays(1)); - NetworkTimeSuggestion timeSuggestion = mScript - .generateNetworkTimeSuggestion(suggestedTime); - - mScript.simulateNetworkTimeSuggestion(timeSuggestion) - .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestNetworkSuggestion(null); + assertNull( + "Telephony suggestion should still be expired:", + mScript.peekBestTelephonySuggestion()); } @Test public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_lowerPriorityComesFirst() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); Instant networkTime = ARBITRARY_TEST_TIME; Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -686,7 +695,8 @@ public class TimeDetectorStrategyImplTest { @Test public void whenAllTimeSuggestionsAreAvailable_higherPriorityWins_higherPriorityComesFirst() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); Instant networkTime = ARBITRARY_TEST_TIME; Instant telephonyTime = ARBITRARY_TEST_TIME.plus(Duration.ofDays(30)); @@ -706,7 +716,8 @@ public class TimeDetectorStrategyImplTest { @Test public void whenHighestPrioritySuggestionIsNotAvailable_fallbacksToNext() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) - .pokeAutoTimeDetectionEnabled(true); + .pokeAutoTimeDetectionEnabled(true) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY, ORIGIN_NETWORK); NetworkTimeSuggestion timeSuggestion = mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME); @@ -720,7 +731,7 @@ public class TimeDetectorStrategyImplTest { public void suggestionsFromSourceNotListedInPrioritiesList_areIgnored() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true) - .pokeAutoOriginPriorities(new int[]{ORIGIN_TELEPHONY}); + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY); NetworkTimeSuggestion timeSuggestion = mScript.generateNetworkTimeSuggestion( ARBITRARY_TEST_TIME); @@ -730,6 +741,19 @@ public class TimeDetectorStrategyImplTest { .verifySystemClockWasNotSetAndResetCallTracking(); } + @Test + public void autoOriginPrioritiesList_doesNotAffectManualSuggestion() { + mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) + .pokeAutoTimeDetectionEnabled(false) + .pokeAutoOriginPriorities(ORIGIN_TELEPHONY); + + ManualTimeSuggestion timeSuggestion = + mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME); + + mScript.simulateManualTimeSuggestion(timeSuggestion, true /* expectedResult */) + .verifySystemClockWasSetAndResetCallTracking(ARBITRARY_TEST_TIME.toEpochMilli()); + } + /** * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving * like the real thing should, it also asserts preconditions. @@ -761,7 +785,7 @@ public class TimeDetectorStrategyImplTest { } @Override - public int[] getAutoOriginPriorities() { + public int[] autoOriginPriorities() { return mAutoOriginPriorities; } @@ -886,8 +910,8 @@ public class TimeDetectorStrategyImplTest { return this; } - Script pokeAutoOriginPriorities(@Origin int[] autoOriginPriorites) { - mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorites); + Script pokeAutoOriginPriorities(@Origin int... autoOriginPriorities) { + mFakeCallback.pokeAutoOriginPriorities(autoOriginPriorities); return this; } diff --git a/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java b/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java new file mode 100644 index 000000000000..590df3c18f5a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/utils/WatchableTester.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * A class to count the number of notifications received. + */ +public class WatchableTester extends Watcher { + + // The count of changes. + public int mChanges = 0; + + // The change count at the last verifyChangeReported() call. + public int mLastChangeCount = 0; + + // The single Watchable that this monitors. + public final Watchable mWatched; + + // The key, used for messages + public String mKey; + + // Clear the changes count, for when the tester is reused. + public void clear() { + mChanges = 0; + } + + /** + * Create the WatchableTester with a Watcher and a key. The key is used for logging + * test failures. + * @param w The {@link Watchable} under test + * @param k A key that is prefixed to any test failures. + **/ + public WatchableTester(Watchable w, String k) { + mWatched = w; + mKey = k; + } + + // Listen for events + public void register() { + mWatched.registerObserver(this); + } + + // Stop listening for events + public void unregister() { + mWatched.unregisterObserver(this); + } + + // Count the number of notifications received. + @Override + public void onChange(Watchable what) { + mChanges++; + } + + // Verify the count. + public void verify(int want, String msg) { + assertEquals(mKey + " " + msg, want, mChanges); + } + + // Verify that at least one change was reported since the last verify. The actual + // number of changes is not important. This resets the count of changes. + public void verifyChangeReported(String msg) { + assertTrue(mKey + " " + msg, mLastChangeCount < mChanges); + mLastChangeCount = mChanges; + } + + // Verify that no change was reported since the last verify. + public void verifyNoChangeReported(String msg) { + assertTrue(mKey + " " + msg, mLastChangeCount == mChanges); + } +} diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java index 40575e4cf16f..9bea9d4cedbd 100644 --- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java @@ -18,15 +18,10 @@ package com.android.server.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.platform.test.annotations.Presubmit; +import static org.junit.Assert.fail; import androidx.test.filters.SmallTest; -import com.android.internal.util.Preconditions; -import com.android.internal.util.TraceBuffer; - import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -42,56 +37,76 @@ import org.junit.Test; @SmallTest public class WatcherTest { + // A counter to generate unique IDs for Leaf elements. + private int mLeafId = 0; + + // Useful indices used int the tests. + private static final int INDEX_A = 1; + private static final int INDEX_B = 2; + private static final int INDEX_C = 3; + private static final int INDEX_D = 4; + // A small Watchable leaf node - private class Leaf extends WatchableImpl { - private int datum = 0; + private class Leaf extends WatchableImpl implements Snappable { + private int mId; + private int mDatum; + + Leaf() { + mDatum = 0; + mId = mLeafId++; + } + void set(int i) { - if (datum != i) { - datum = i; + if (mDatum != i) { + mDatum = i; dispatchChange(this); } } + int get() { + return mDatum; + } void tick() { - set(datum + 1); + set(mDatum + 1); } - } - - // A top-most watcher. It counts the number of notifications that it receives. - private class Tester extends Watcher { - // The count of changes. - public int changes = 0; - - // The single Watchable that this monitors. - public final Watchable mWatched; - - // The key, used for messages - public String mKey; - - // Create the Tester with a Watcher - public Tester(Watchable w, String k) { - mWatched = w; - mKey = k; + public Leaf snapshot() { + Leaf result = new Leaf(); + result.mDatum = mDatum; + result.mId = mId; + result.seal(); + return result; } - - // Listen for events - public void register() { - mWatched.registerObserver(this); + @Override + public boolean equals(Object o) { + if (o instanceof Leaf) { + return mDatum == ((Leaf) o).mDatum && mId == ((Leaf) o).mId; + } else { + return false; + } } - - // Stop listening for events - public void unregister() { - mWatched.unregisterObserver(this); + @Override + public String toString() { + return "Leaf(" + mDatum + "," + mId + ")"; } + } - // Count the number of notifications received. - @Override - public void onChange(Watchable what) { - changes++; + // Execute the {@link Runnable} and if {@link UnsupportedOperationException} is + // thrown, do nothing. If no exception is thrown, fail the test. + private void verifySealed(String msg, Runnable test) { + try { + test.run(); + fail(msg + " should be sealed"); + } catch (IllegalStateException e) { + // The exception was expected. } + } - // Verify the count. - public void verify(int want, String msg) { - assertEquals(mKey + " " + msg, want, changes); + // Execute the {@link Runnable} and if {@link UnsupportedOperationException} is + // thrown, fail the test. If no exception is thrown, do nothing. + private void verifyNotSealed(String msg, Runnable test) { + try { + test.run(); + } catch (IllegalStateException e) { + fail(msg + " should be not sealed"); } } @@ -104,168 +119,212 @@ public class WatcherTest { } @Test - public void test_notify() { - - Tester tester; + public void testBasicBehavior() { + WatchableTester tester; // Create a few leaves - Leaf a = new Leaf(); - Leaf b = new Leaf(); - Leaf c = new Leaf(); - Leaf d = new Leaf(); + Leaf leafA = new Leaf(); // Basic test. Create a leaf and verify that changes to the leaf get notified to // the tester. - tester = new Tester(a, "Leaf"); + tester = new WatchableTester(leafA, "Leaf"); tester.verify(0, "Initial leaf - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - a.tick(); - a.tick(); + leafA.tick(); + leafA.tick(); tester.verify(3, "Updates with registration"); + // Create a snapshot. Verify that the snapshot matches the + Leaf leafASnapshot = leafA.snapshot(); + assertEquals("Leaf snapshot", leafA.get(), leafASnapshot.get()); + leafA.tick(); + assertTrue(leafA.get() != leafASnapshot.get()); + tester.verify(4, "Tick after snapshot"); + verifySealed("Leaf", ()->leafASnapshot.tick()); // Add the same leaf to more than one tester. Verify that a change to the leaf is seen by // all registered listeners. - Tester buddy1 = new Tester(a, "Leaf2"); - Tester buddy2 = new Tester(a, "Leaf3"); + tester.clear(); + WatchableTester buddy1 = new WatchableTester(leafA, "Leaf2"); + WatchableTester buddy2 = new WatchableTester(leafA, "Leaf3"); buddy1.verify(0, "Initial leaf - no registration"); buddy2.verify(0, "Initial leaf - no registration"); - a.tick(); - tester.verify(4, "Updates with buddies"); + leafA.tick(); + tester.verify(1, "Updates with buddies"); buddy1.verify(0, "Updates - no registration"); buddy2.verify(0, "Updates - no registration"); buddy1.register(); buddy2.register(); buddy1.verify(0, "No updates - registered"); buddy2.verify(0, "No updates - registered"); - a.tick(); + leafA.tick(); buddy1.verify(1, "First update"); buddy2.verify(1, "First update"); buddy1.unregister(); - a.tick(); + leafA.tick(); buddy1.verify(1, "Second update - unregistered"); buddy2.verify(2, "Second update"); + } - buddy1 = null; - buddy2 = null; + @Test + public void testWatchedArrayMap() { + WatchableTester tester; - final int INDEX_A = 1; - final int INDEX_B = 2; - final int INDEX_C = 3; - final int INDEX_D = 4; + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); // Test WatchedArrayMap - WatchedArrayMap<Integer, Leaf> am = new WatchedArrayMap<>(); - am.put(INDEX_A, a); - am.put(INDEX_B, b); - tester = new Tester(am, "WatchedArrayMap"); + WatchedArrayMap<Integer, Leaf> array = new WatchedArrayMap<>(); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + tester = new WatchableTester(array, "WatchedArrayMap"); tester.verify(0, "Initial array - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - b.tick(); + leafB.tick(); tester.verify(2, "Updates with registration"); - am.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(3, "Removed b"); - b.tick(); + leafB.tick(); tester.verify(3, "Updates with b not watched"); - am.put(INDEX_B, b); - am.put(INDEX_C, b); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafB); tester.verify(5, "Added b twice"); - b.tick(); + leafB.tick(); tester.verify(6, "Changed b - single notification"); - am.remove(INDEX_C); + array.remove(INDEX_C); tester.verify(7, "Removed first b"); - b.tick(); + leafB.tick(); tester.verify(8, "Changed b - single notification"); - am.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(9, "Removed second b"); - b.tick(); + leafB.tick(); tester.verify(9, "Updated b - no change"); - am.clear(); + array.clear(); tester.verify(10, "Cleared array"); - b.tick(); + leafB.tick(); tester.verify(10, "Change to b not in array"); // Special methods - am.put(INDEX_C, c); + array.put(INDEX_C, leafC); tester.verify(11, "Added c"); - c.tick(); + leafC.tick(); tester.verify(12, "Ticked c"); - am.setValueAt(am.indexOfKey(INDEX_C), d); + array.setValueAt(array.indexOfKey(INDEX_C), leafD); tester.verify(13, "Replaced c with d"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(14, "Ticked d and c (c not registered)"); - am = null; + // Snapshot + { + final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot(); + tester.verify(14, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedArrayMap snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue("WatchedArrayMap elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + assertTrue("WatchedArrayMap element copy", + array.valueAt(i).equals(arraySnap.valueAt(i))); + } + leafD.tick(); + tester.verify(15, "Tick after snapshot"); + // Verify that the snapshot is sealed + verifySealed("WatchedArrayMap", ()->arraySnap.put(INDEX_A, leafA)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + } + + @Test + public void testWatchedSparseArray() { + WatchableTester tester; + + // Create a few leaves + Leaf leafA = new Leaf(); + Leaf leafB = new Leaf(); + Leaf leafC = new Leaf(); + Leaf leafD = new Leaf(); // Test WatchedSparseArray - WatchedSparseArray<Leaf> sa = new WatchedSparseArray<>(); - sa.put(INDEX_A, a); - sa.put(INDEX_B, b); - tester = new Tester(sa, "WatchedSparseArray"); + WatchedSparseArray<Leaf> array = new WatchedSparseArray<>(); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + tester = new WatchableTester(array, "WatchedSparseArray"); tester.verify(0, "Initial array - no registration"); - a.tick(); + leafA.tick(); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - a.tick(); + leafA.tick(); tester.verify(1, "Updates with registration"); - b.tick(); + leafB.tick(); tester.verify(2, "Updates with registration"); - sa.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(3, "Removed b"); - b.tick(); + leafB.tick(); tester.verify(3, "Updates with b not watched"); - sa.put(INDEX_B, b); - sa.put(INDEX_C, b); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafB); tester.verify(5, "Added b twice"); - b.tick(); + leafB.tick(); tester.verify(6, "Changed b - single notification"); - sa.remove(INDEX_C); + array.remove(INDEX_C); tester.verify(7, "Removed first b"); - b.tick(); + leafB.tick(); tester.verify(8, "Changed b - single notification"); - sa.remove(INDEX_B); + array.remove(INDEX_B); tester.verify(9, "Removed second b"); - b.tick(); - tester.verify(9, "Updated b - no change"); - sa.clear(); + leafB.tick(); + tester.verify(9, "Updated leafB - no change"); + array.clear(); tester.verify(10, "Cleared array"); - b.tick(); + leafB.tick(); tester.verify(10, "Change to b not in array"); // Special methods - sa.put(INDEX_A, a); - sa.put(INDEX_B, b); - sa.put(INDEX_C, c); + array.put(INDEX_A, leafA); + array.put(INDEX_B, leafB); + array.put(INDEX_C, leafC); tester.verify(13, "Added c"); - c.tick(); + leafC.tick(); tester.verify(14, "Ticked c"); - sa.setValueAt(sa.indexOfKey(INDEX_C), d); + array.setValueAt(array.indexOfKey(INDEX_C), leafD); tester.verify(15, "Replaced c with d"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(16, "Ticked d and c (c not registered)"); - sa.append(INDEX_D, c); + array.append(INDEX_D, leafC); tester.verify(17, "Append c"); - c.tick(); - d.tick(); + leafC.tick(); + leafD.tick(); tester.verify(19, "Ticked d and c"); - assertEquals("Verify four elements", 4, sa.size()); + assertEquals("Verify four elements", 4, array.size()); // Figure out which elements are at which indices. Leaf[] x = new Leaf[4]; for (int i = 0; i < 4; i++) { - x[i] = sa.valueAt(i); + x[i] = array.valueAt(i); } - sa.removeAtRange(0, 2); + array.removeAtRange(0, 2); tester.verify(20, "Removed two elements in one operation"); x[0].tick(); x[1].tick(); @@ -274,31 +333,77 @@ public class WatcherTest { x[3].tick(); tester.verify(22, "Ticked two remaining elements"); - sa = null; + // Snapshot + { + final WatchedSparseArray<Leaf> arraySnap = array.snapshot(); + tester.verify(22, "Generate snapshot (no changes)"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + for (int j = 0; j < arraySnap.size(); j++) { + assertTrue("WatchedSparseArray elements differ", + array.valueAt(i) != arraySnap.valueAt(j)); + } + assertTrue("WatchedArrayMap element copy", + array.valueAt(i).equals(arraySnap.valueAt(i))); + } + leafD.tick(); + tester.verify(23, "Tick after snapshot"); + // Verify that the array snapshot is sealed + verifySealed("WatchedSparseArray", ()->arraySnap.put(INDEX_A, leafB)); + } + // Recreate the snapshot since the test corrupted it. + { + final WatchedSparseArray<Leaf> arraySnap = array.snapshot(); + // Verify that elements are also snapshots + final Leaf arraySnapElement = arraySnap.valueAt(0); + verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick()); + } + } + + @Test + public void testWatchedSparseBooleanArray() { + WatchableTester tester; // Test WatchedSparseBooleanArray - WatchedSparseBooleanArray sb = new WatchedSparseBooleanArray(); - tester = new Tester(sb, "WatchedSparseBooleanArray"); + WatchedSparseBooleanArray array = new WatchedSparseBooleanArray(); + tester = new WatchableTester(array, "WatchedSparseBooleanArray"); tester.verify(0, "Initial array - no registration"); - sb.put(INDEX_A, true); + array.put(INDEX_A, true); tester.verify(0, "Updates with no registration"); tester.register(); tester.verify(0, "Updates with no registration"); - sb.put(INDEX_B, true); + array.put(INDEX_B, true); tester.verify(1, "Updates with registration"); - sb.put(INDEX_B, true); + array.put(INDEX_B, true); tester.verify(1, "Null update"); - sb.put(INDEX_B, false); - sb.put(INDEX_C, true); + array.put(INDEX_B, false); + array.put(INDEX_C, true); tester.verify(3, "Updates with registration"); // Special methods - sb.put(INDEX_C, true); + array.put(INDEX_C, true); tester.verify(3, "Added true, no change"); - sb.setValueAt(sb.indexOfKey(INDEX_C), false); + array.setValueAt(array.indexOfKey(INDEX_C), false); tester.verify(4, "Replaced true with false"); - sb.append(INDEX_D, true); + array.append(INDEX_D, true); tester.verify(5, "Append true"); - sb = null; + // Snapshot + { + WatchedSparseBooleanArray arraySnap = array.snapshot(); + tester.verify(5, "Generate snapshot"); + // Verify that the snapshot is a proper copy of the source. + assertEquals("WatchedSparseBooleanArray snap same size", + array.size(), arraySnap.size()); + for (int i = 0; i < array.size(); i++) { + assertEquals("WatchedSparseArray element copy", + array.valueAt(i), arraySnap.valueAt(i)); + } + array.put(INDEX_D, false); + tester.verify(6, "Tick after snapshot"); + // Verify that the array is sealed + verifySealed("WatchedSparseBooleanArray", ()->arraySnap.put(INDEX_D, false)); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java index 2904a5b73646..a67f64596ef5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java +++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.wm.utils; +package com.android.server.testutils; import android.annotation.NonNull; import android.provider.DeviceConfig; @@ -22,6 +22,7 @@ import android.util.ArrayMap; import android.util.Pair; import com.android.internal.util.Preconditions; +import com.android.server.utils.DeviceConfigInterface; import java.lang.reflect.Constructor; import java.util.HashMap; @@ -122,6 +123,19 @@ public class FakeDeviceConfigInterface implements DeviceConfigInterface { } @Override + public float getFloat(String namespace, String name, float defaultValue) { + String value = getProperty(namespace, name); + if (value == null) { + return defaultValue; + } + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + @Override public boolean getBoolean(String namespace, String name, boolean defaultValue) { String value = getProperty(namespace, name); return value != null ? Boolean.parseBoolean(value) : defaultValue; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index c50792866582..4a4d9bc174fb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -54,6 +54,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; import android.util.SparseArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.util.FastXmlSerializer; @@ -267,7 +269,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); // approved services aren't in xml - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(new byte[]{})), null); writeExpectedValuesToSettings(approvalLevel); @@ -335,7 +337,7 @@ public class ManagedServicesTest extends UiServiceTestCase { String testComponent = "user.test.component/C1"; String resolvedValue = (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; - XmlPullParser parser = + TypedXmlPullParser parser = getParserWithEntries(service, getXmlEntry(resolvedValue, 0, true)); service.readXml(parser, null, true, 10); @@ -357,7 +359,7 @@ public class ManagedServicesTest extends UiServiceTestCase { String resolvedValue = (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage; String xmlEntry = getXmlEntry(resolvedValue, 0, true, false); - XmlPullParser parser = getParserWithEntries(service, xmlEntry); + TypedXmlPullParser parser = getParserWithEntries(service, xmlEntry); service.readXml(parser, null, true, 0); @@ -384,7 +386,7 @@ public class ManagedServicesTest extends UiServiceTestCase { ManagedServices service2 = new TestManagedServices( getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); BufferedOutputStream outStream = new BufferedOutputStream(baos); serializer.setOutput(outStream, "utf-8"); @@ -396,7 +398,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); outStream.flush(); - final XmlPullParser parser = Xml.newPullParser(); + final TypedXmlPullParser parser = Xml.newFastPullParser(); BufferedInputStream input = new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())); @@ -536,7 +538,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service, Collections.singletonList(service.getPackageName(resolvedValue10)), 10); - XmlPullParser parser = + TypedXmlPullParser parser = getParserWithEntries( service, getXmlEntry(resolvedValue0, 0, true), @@ -544,7 +546,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service.readXml(parser, null, false, UserHandle.USER_ALL); // Write backup. - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -557,7 +559,7 @@ public class ManagedServicesTest extends UiServiceTestCase { service.setPackageOrComponentEnabled(resolvedValue10, 10, true, false); // Parse backup via restore. - XmlPullParser restoreParser = Xml.newPullParser(); + TypedXmlPullParser restoreParser = Xml.newFastPullParser(); restoreParser.setInput( new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); restoreParser.nextTag(); @@ -608,7 +610,7 @@ public class ManagedServicesTest extends UiServiceTestCase { addExpectedServices(service, entriesExpectedToHaveServices, userInfo.id); } - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -618,7 +620,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -640,7 +642,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); loadXml(service); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -666,7 +668,7 @@ public class ManagedServicesTest extends UiServiceTestCase { mIpm, approvalLevel); loadXml(service); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -674,7 +676,7 @@ public class ManagedServicesTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); byte[] rawOutput = baos.toByteArray(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(rawOutput)), null); @@ -1387,7 +1389,7 @@ public class ManagedServicesTest extends UiServiceTestCase { private void loadXml(ManagedServices service) throws Exception { String xmlString = createXml(service); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xmlString.getBytes())), null); parser.nextTag(); @@ -1423,7 +1425,7 @@ public class ManagedServicesTest extends UiServiceTestCase { return xml.toString(); } - private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) + private TypedXmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries) throws Exception { final StringBuffer xml = new StringBuffer(); xml.append("<" + service.getConfig().xmlTag + ">\n"); @@ -1432,7 +1434,7 @@ public class ManagedServicesTest extends UiServiceTestCase { } xml.append("</" + service.getConfig().xmlTag + ">"); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java index f649911b6bb9..b3116d970725 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java @@ -36,6 +36,7 @@ import android.content.pm.UserInfo; import android.os.UserHandle; import android.os.UserManager; import android.util.IntArray; +import android.util.TypedXmlPullParser; import android.util.Xml; import com.android.server.UiServiceTestCase; @@ -45,7 +46,6 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlPullParser; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -126,7 +126,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase { + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />" + "</enabled_assistants>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.toString().getBytes())), null); parser.nextTag(); @@ -142,24 +142,24 @@ public class NotificationAssistantsTest extends UiServiceTestCase { ComponentName component1 = ComponentName.unflattenFromString("package/Component1"); ComponentName component2 = ComponentName.unflattenFromString("package/Component2"); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal( - any(ComponentName.class), eq(mZero.id), anyBoolean()); + any(ComponentName.class), eq(mZero.id), anyBoolean(), anyBoolean()); mAssistants.setPackageOrComponentEnabled(component2.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, times(1)).setNotificationAssistantAccessGrantedForUserInternal( - component1, mZero.id, false); + component1, mZero.id, false, true); } @Test public void testSetPackageOrComponentEnabled_samePackage() throws Exception { ComponentName component1 = ComponentName.unflattenFromString("package/Component1"); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); mAssistants.setPackageOrComponentEnabled(component1.flattenToString(), mZero.id, true, - true); + true, true); verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal( - any(ComponentName.class), eq(mZero.id), anyBoolean()); + any(ComponentName.class), eq(mZero.id), anyBoolean(), anyBoolean()); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 09a4289ece3f..5cf529a239dc 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -160,6 +160,8 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.widget.RemoteViews; @@ -170,7 +172,6 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.logging.InstanceIdSequence; import com.android.internal.logging.InstanceIdSequenceFake; import com.android.internal.statusbar.NotificationVisibility; -import com.android.internal.util.FastXmlSerializer; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.SystemService; @@ -194,8 +195,6 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -355,12 +354,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Override protected void setNotificationAssistantAccessGrantedForUserInternal( - ComponentName assistant, int userId, boolean granted) { + ComponentName assistant, int userId, boolean granted, boolean userSet) { if (mNotificationAssistantAccessGrantedCallback != null) { - mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted); + mNotificationAssistantAccessGrantedCallback.onGranted(assistant, userId, granted, + userSet); return; } - super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted); + super.setNotificationAssistantAccessGrantedForUserInternal(assistant, userId, granted, + userSet); } @Override @@ -374,7 +375,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } interface NotificationAssistantAccessGrantedCallback { - void onGranted(ComponentName assistant, int userId, boolean granted); + void onGranted(ComponentName assistant, int userId, boolean granted, boolean userSet); } } @@ -897,7 +898,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(userId); verify(mAssistants).setPackageOrComponentEnabled( - eq(testComponent), eq(userId), eq(true), eq(true)); + eq(testComponent), eq(userId), eq(true), eq(true), eq(false)); } @Test @@ -2907,7 +2908,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, true); + c.flattenToString(), user.getIdentifier(), true, true, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, true); @@ -2951,7 +2952,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), user.getIdentifier(), true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -2966,7 +2967,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -2981,7 +2982,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3003,9 +3004,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(c, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 10, true, true); + c.flattenToString(), 10, true, true, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -3029,7 +3030,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mBinderService.setNotificationAssistantAccessGranted(null, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, false); + c.flattenToString(), 0, true, false, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, false); verify(mListeners, never()).setPackageOrComponentEnabled( @@ -3053,7 +3054,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, user.getIdentifier(), true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, false); + c.flattenToString(), user.getIdentifier(), true, false, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), user.getIdentifier(), false, false); @@ -3082,9 +3083,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { null, user.getIdentifier(), true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), user.getIdentifier(), true, false); + c.flattenToString(), user.getIdentifier(), true, false, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), ui10.id, true, false); + c.flattenToString(), ui10.id, true, false, true); verify(mAssistants).setUserSet(0, true); verify(mAssistants).setUserSet(10, true); verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( @@ -3104,7 +3105,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); verify(mListeners, never()).setPackageOrComponentEnabled( any(), anyInt(), anyBoolean(), anyBoolean()); } @@ -3189,7 +3190,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.flattenToString(), 0, false, true); verify(mAssistants, times(1)).setPackageOrComponentEnabled( - c.flattenToString(), 0, true, true); + c.flattenToString(), 0, true, true, true); } @Test @@ -3205,7 +3206,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( c.getPackageName(), 0, true, true); verify(mAssistants, never()).setPackageOrComponentEnabled( - any(), anyInt(), anyBoolean(), anyBoolean()); + any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); } @Test @@ -3446,7 +3447,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false, UserHandle.USER_ALL); - verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong()); + verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong()); } @Test @@ -3887,12 +3888,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); channel.setSound(Uri.EMPTY, null); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); channel.writeXmlForBackup(serializer, getContext()); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); @@ -3915,7 +3916,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); channel.setVibrationPattern(new long[0]); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); channel.writeXml(serializer); @@ -5417,7 +5418,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(xmlConfig), eq(0), eq(true)); + .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false)); } @Test @@ -5439,7 +5440,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(deviceConfig), eq(0), eq(true)); + .onGranted(eq(deviceConfig), eq(0), eq(true), eq(false)); } @Test @@ -5462,7 +5463,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.setDefaultAssistantForUser(0); verify(mNotificationAssistantAccessGrantedCallback) - .onGranted(eq(xmlConfig), eq(0), eq(true)); + .onGranted(eq(xmlConfig), eq(0), eq(true), eq(false)); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 7ec8689e5387..98c4a2da6a4f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -99,6 +99,8 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Pair; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.InstrumentationRegistry; @@ -280,7 +282,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { private ByteArrayOutputStream writeXmlAndPurge( String pkg, int uid, boolean forBackup, int userId, String... channelIds) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -300,7 +302,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { private void loadByteArrayXml(byte[] byteArray, boolean forRestore, int userId) throws Exception { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null); parser.nextTag(); mHelper.readXml(parser, forRestore, userId); @@ -717,7 +719,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{ UID_N_MR1}); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -772,7 +774,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\"" + Notification.VISIBILITY_PRIVATE + "\" />\n" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), null); parser.nextTag(); @@ -2559,7 +2561,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2580,7 +2582,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2612,7 +2614,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + " importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2753,7 +2755,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -2779,7 +2781,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"c\" name=\"c\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3047,7 +3049,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { toAdd.add(new Pair(PKG_O, UID_O)); mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, toAdd); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -3121,7 +3123,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3154,12 +3156,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</ranking>"; // trigger a restore for both users - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser0.getBytes())), null); parser.nextTag(); mHelper.readXml(parser, true, 0); - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser10.getBytes())), null); parser.nextTag(); @@ -3242,7 +3244,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3261,7 +3263,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -3280,7 +3282,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>" + "</package>" + "</ranking>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 3deeea2d4577..35b224a24061 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -43,6 +43,8 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.util.IntArray; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -95,7 +97,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); @@ -114,12 +116,12 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); ByteArrayOutputStream baos = new ByteArrayOutputStream(); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); mSnoozeHelper.writeXml(serializer); @@ -137,7 +139,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<context version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 1); @@ -151,7 +153,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { throws XmlPullParserException, IOException { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 999999999); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -159,7 +161,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); mSnoozeHelper.readXml(parser, 1); @@ -175,7 +177,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 0); // Thread.sleep(100); - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -183,7 +185,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { serializer.endDocument(); serializer.flush(); Thread.sleep(10); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), "utf-8"); mSnoozeHelper.readXml(parser, 2); @@ -227,7 +229,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" " + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>" + "</snoozed-notifications>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml_string.getBytes())), null); mSnoozeHelper.readXml(parser, 4); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index 013a99433041..5262465a399c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -30,6 +30,8 @@ import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeConfig.EventInfo; import android.service.notification.ZenPolicy; import android.test.suitebuilder.annotation.SmallTest; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import androidx.test.runner.AndroidJUnit4; @@ -199,7 +201,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { rule.name = "name"; rule.snoozing = true; - XmlSerializer out = new FastXmlSerializer(); + TypedXmlSerializer out = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); out.setOutput(new BufferedOutputStream(baos), "utf-8"); out.startDocument(null, true); @@ -208,7 +210,7 @@ public class ZenModeConfigTest extends UiServiceTestCase { out.endTag(null, tag); out.endDocument(); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 3430dbdce753..cfdd2464322d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -95,6 +95,8 @@ import android.testing.TestableLooper; import android.util.ArrayMap; import android.util.Log; import android.util.StatsEvent; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import com.android.internal.R; @@ -189,14 +191,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "&end=7.0&exitAtAlarm=true\"/>" + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); return new XmlResourceParserImpl(parser); } private ByteArrayOutputStream writeXmlAndPurge(Integer version) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -209,7 +211,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId) throws Exception { - XmlSerializer serializer = new FastXmlSerializer(); + TypedXmlSerializer serializer = Xml.newFastSerializer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); serializer.startDocument(null, true); @@ -222,8 +224,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { return baos; } - private XmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception { - XmlPullParser parser = Xml.newPullParser(); + private TypedXmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception { + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput( new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -837,7 +839,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurge(null); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL); assertEquals("Config mismatch: current vs expected: " @@ -962,7 +964,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelperSpy.mConfigs.put(11, newConfig11); // Parse backup data. - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, 10); mZenModeHelperSpy.readXml(parser, true, 11); @@ -980,7 +982,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig original = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); assertEquals("Config mismatch: current vs original: " @@ -1000,7 +1002,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); // Restore data for user 10. - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, 10); ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10); @@ -1045,7 +1047,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurge(null); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1086,7 +1088,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy(); ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM); - XmlPullParser parser = getParserForByteStream(baos); + TypedXmlPullParser parser = getParserForByteStream(baos); mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM); ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId); @@ -1113,7 +1115,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1133,7 +1135,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1149,7 +1151,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1168,7 +1170,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1187,7 +1189,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1206,7 +1208,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1222,7 +1224,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { + "<disallow visualEffects=\"511\" />" + "</zen>"; - parser = Xml.newPullParser(); + parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(xml.getBytes())), null); parser.nextTag(); @@ -1242,7 +1244,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1278,7 +1280,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1330,7 +1332,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1399,7 +1401,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // set previous version ByteArrayOutputStream baos = writeXmlAndPurge(5); - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); parser.nextTag(); @@ -1628,12 +1630,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { } /** - * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml() + * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml() */ final class XmlResourceParserImpl implements XmlResourceParser { - private XmlPullParser parser; + private TypedXmlPullParser parser; - public XmlResourceParserImpl(XmlPullParser parser) { + public XmlResourceParserImpl(TypedXmlPullParser parser) { this.parser = parser; } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 7f4f3dd58812..0ed037c7e70a 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -65,8 +65,6 @@ android:turnScreenOn="true" /> <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ResumeWhilePausingActivity" android:resumeWhilePausing="true"/> - <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" - android:showWhenLocked="true" android:allowEmbedded="true"/> <activity android:name="com.android.server.wm.ActivityLeakTests$DetectLeakActivity" /> </application> diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java index 9a6eb1cf2623..99bd0d7198c0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java @@ -68,16 +68,16 @@ public class ActivityDisplayTests extends WindowTestsBase { mRootWindowContainer.getDefaultDisplay().getDefaultTaskDisplayArea(); final Task stack = new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build(); - final Task prevFocusedStack = taskDisplayAreas.getFocusedStack(); + final Task prevFocusedStack = taskDisplayAreas.getFocusedRootTask(); stack.moveToFront("moveStackToFront"); // After moving the stack to front, the previous focused should be the last focused. assertTrue(stack.isFocusedStackOnDisplay()); - assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedStack()); + assertEquals(prevFocusedStack, taskDisplayAreas.getLastFocusedRootTask()); stack.moveToBack("moveStackToBack", null /* task */); // After moving the stack to back, the stack should be the last focused. - assertEquals(stack, taskDisplayAreas.getLastFocusedStack()); + assertEquals(stack, taskDisplayAreas.getLastFocusedRootTask()); } /** @@ -88,7 +88,7 @@ public class ActivityDisplayTests extends WindowTestsBase { public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() { // Create a pinned stack and move to front. final Task pinnedStack = mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); + .createRootTask(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task pinnedTask = new TaskBuilder(mAtm.mTaskSupervisor) .setParentTask(pinnedStack).build(); new ActivityBuilder(mAtm).setActivityFlags(FLAG_ALWAYS_FOCUSABLE) @@ -108,7 +108,7 @@ public class ActivityDisplayTests extends WindowTestsBase { } /** - * Test {@link TaskDisplayArea#mPreferredTopFocusableStack} will be cleared when + * Test {@link TaskDisplayArea#mPreferredTopFocusableRootTask} will be cleared when * the stack is removed or moved to back, and the focused stack will be according to z-order. */ @Test @@ -128,7 +128,7 @@ public class ActivityDisplayTests extends WindowTestsBase { assertTrue(stack1.isFocusedStackOnDisplay()); // Stack2 should be focused after removing stack1. - stack1.getDisplayArea().removeStack(stack1); + stack1.getDisplayArea().removeRootTask(stack1); assertTrue(stack2.isFocusedStackOnDisplay()); } @@ -155,12 +155,12 @@ public class ActivityDisplayTests extends WindowTestsBase { display.remove(); // The removed display should have no focused stack and its home stack should never resume. - assertNull(display.getFocusedStack()); + assertNull(display.getFocusedRootTask()); verify(homeStack, never()).resumeTopActivityUncheckedLocked(any(), any()); } private Task createFullscreenStackWithSimpleActivityAt(DisplayContent display) { - final Task fullscreenStack = display.getDefaultTaskDisplayArea().createStack( + final Task fullscreenStack = display.getDefaultTaskDisplayArea().createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task fullscreenTask = new TaskBuilder(mAtm.mTaskSupervisor) .setParentTask(fullscreenStack).build(); @@ -191,7 +191,7 @@ public class ActivityDisplayTests extends WindowTestsBase { // Move stack with activity to top. stack.moveToFront("testStackToFront"); - assertEquals(stack, display.getFocusedStack()); + assertEquals(stack, display.getFocusedRootTask()); assertEquals(activity, display.topRunningActivity()); assertNull(display.topRunningActivity(true /* considerKeyguardState */)); @@ -207,7 +207,7 @@ public class ActivityDisplayTests extends WindowTestsBase { // Move empty stack to front. The running activity in focusable stack which below the // empty stack should be returned. emptyStack.moveToFront("emptyStackToFront"); - assertEquals(stack, display.getFocusedStack()); + assertEquals(stack, display.getFocusedRootTask()); assertTopRunningActivity(showWhenLockedActivity, display); } @@ -222,7 +222,7 @@ public class ActivityDisplayTests extends WindowTestsBase { @Test public void testAlwaysOnTopStackLocation() { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task alwaysOnTopStack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM, + final Task alwaysOnTopStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mAtm) .setTask(alwaysOnTopStack).build(); @@ -232,31 +232,31 @@ public class ActivityDisplayTests extends WindowTestsBase { assertTrue(alwaysOnTopStack.isAlwaysOnTop()); // Ensure always on top state is synced to the children of the stack. assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop()); - assertEquals(alwaysOnTopStack, taskDisplayArea.getTopStack()); + assertEquals(alwaysOnTopStack, taskDisplayArea.getTopRootTask()); - final Task pinnedStack = taskDisplayArea.createStack( + final Task pinnedStack = taskDisplayArea.createRootTask( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(pinnedStack, taskDisplayArea.getRootPinnedTask()); - assertEquals(pinnedStack, taskDisplayArea.getTopStack()); + assertEquals(pinnedStack, taskDisplayArea.getTopRootTask()); - final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack( + final Task anotherAlwaysOnTopStack = taskDisplayArea.createRootTask( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); anotherAlwaysOnTopStack.setAlwaysOnTop(true); taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack, false /* includingParents */); assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - int topPosition = taskDisplayArea.getStackCount() - 1; + int topPosition = taskDisplayArea.getRootTaskCount() - 1; // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the // existing alwaysOnTop stack. - assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 1)); - final Task nonAlwaysOnTopStack = taskDisplayArea.createStack( + final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask( WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(taskDisplayArea, nonAlwaysOnTopStack.getDisplayArea()); - topPosition = taskDisplayArea.getStackCount() - 1; + topPosition = taskDisplayArea.getRootTaskCount() - 1; // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the // existing other non-alwaysOnTop stacks. - assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3)); + assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 3)); anotherAlwaysOnTopStack.setAlwaysOnTop(false); taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack, @@ -264,37 +264,37 @@ public class ActivityDisplayTests extends WindowTestsBase { assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); // Ensure, when always on top is turned off for a stack, the stack is put just below all // other always on top stacks. - assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 2)); anotherAlwaysOnTopStack.setAlwaysOnTop(true); // Ensure always on top state changes properly when windowing mode changes. anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN); assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 2)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 2)); anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM); assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop()); - assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 1)); + assertEquals(anotherAlwaysOnTopStack, taskDisplayArea.getRootTaskAt(topPosition - 1)); - final Task dreamStack = taskDisplayArea.createStack( + final Task dreamStack = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */); assertEquals(taskDisplayArea, dreamStack.getDisplayArea()); assertTrue(dreamStack.isAlwaysOnTop()); - topPosition = taskDisplayArea.getStackCount() - 1; + topPosition = taskDisplayArea.getRootTaskCount() - 1; // Ensure dream shows above all activities, including PiP - assertEquals(dreamStack, taskDisplayArea.getTopStack()); - assertEquals(pinnedStack, taskDisplayArea.getStackAt(topPosition - 1)); + assertEquals(dreamStack, taskDisplayArea.getTopRootTask()); + assertEquals(pinnedStack, taskDisplayArea.getRootTaskAt(topPosition - 1)); - final Task assistStack = taskDisplayArea.createStack( + final Task assistStack = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */); assertEquals(taskDisplayArea, assistStack.getDisplayArea()); assertFalse(assistStack.isAlwaysOnTop()); - topPosition = taskDisplayArea.getStackCount() - 1; + topPosition = taskDisplayArea.getRootTaskCount() - 1; // Ensure Assistant shows as a non-always-on-top activity when config_assistantOnTopOfDream // is false and on top of everything when true. final boolean isAssistantOnTop = mContext.getResources() .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream); - assertEquals(assistStack, taskDisplayArea.getStackAt( + assertEquals(assistStack, taskDisplayArea.getRootTaskAt( isAssistantOnTop ? topPosition : topPosition - 4)); } @@ -312,13 +312,13 @@ public class ActivityDisplayTests extends WindowTestsBase { private void removeStackTests(Runnable runnable) { final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task stack1 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task stack1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task stack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack3 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task stack3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); - final Task stack4 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task stack4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP); final Task task1 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack1).build(); final Task task2 = new TaskBuilder(mAtm.mTaskSupervisor).setParentTask(stack2).build(); @@ -333,7 +333,7 @@ public class ActivityDisplayTests extends WindowTestsBase { // Removing stacks from the display while removing stacks. doAnswer(invocation -> { - taskDisplayArea.removeStack(stack2); + taskDisplayArea.removeRootTask(stack2); return true; }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); 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 53ade0ea64be..2f34f708a562 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -71,6 +71,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; @@ -154,7 +155,7 @@ public class ActivityRecordTests extends WindowTestsBase { final Task rootTask = activity.getRootTask(); rootTask.removeChild(task, null /*reason*/); // parentTask should be gone on task removal. - assertNull(mAtm.mRootWindowContainer.getStack(rootTask.mTaskId)); + assertNull(mAtm.mRootWindowContainer.getRootTask(rootTask.mTaskId)); } @Test @@ -376,6 +377,28 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test + public void testDestroyedActivityNotScheduleConfigChanged() throws RemoteException { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setCreateTask(true) + .setConfigChanges(CONFIG_ORIENTATION) + .build(); + final Task task = activity.getTask(); + activity.setState(DESTROYED, "Testing"); + clearInvocations(mAtm.getLifecycleManager()); + + final Configuration newConfig = new Configuration(task.getConfiguration()); + newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT + ? ORIENTATION_LANDSCAPE + : ORIENTATION_PORTRAIT; + task.onRequestedOverrideConfigurationChanged(newConfig); + + ensureActivityConfiguration(activity); + + verify(mAtm.getLifecycleManager(), never()) + .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class)); + } + + @Test public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { final ActivityRecord activity = new ActivityBuilder(mAtm) .setCreateTask(true) @@ -386,6 +409,7 @@ public class ActivityRecordTests extends WindowTestsBase { activity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), activity.getConfiguration())); + clearInvocations(mAtm.getLifecycleManager()); final Configuration newConfig = new Configuration(activity.getConfiguration()); final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp); final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp); @@ -401,7 +425,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Mimic the behavior that display doesn't handle app's requested orientation. final DisplayContent dc = activity.getTask().getDisplayContent(); - doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(dc).onDescendantOrientationChanged(any()); doReturn(false).when(dc).handlesOrientationChangeFromDescendant(); final int requestedOrientation; @@ -574,6 +598,7 @@ public class ActivityRecordTests extends WindowTestsBase { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); try { + clearInvocations(mAtm.getLifecycleManager()); doReturn(false).when(stack).isTranslucent(any()); assertTrue(task.shouldBeVisible(null /* starting */)); @@ -853,7 +878,7 @@ public class ActivityRecordTests extends WindowTestsBase { topRootableTask.moveToFront("test"); assertTrue(topRootableTask.isTopStackInDisplayArea()); assertEquals(topRootableTask, topActivityOnNonTopDisplay.getDisplayArea() - .mPreferredTopFocusableStack); + .mPreferredTopFocusableRootTask); final ActivityRecord secondaryDisplayActivity = createActivityOnDisplay(false /* defaultDisplay */, null /* process */); @@ -861,7 +886,7 @@ public class ActivityRecordTests extends WindowTestsBase { topRootableTask.moveToFront("test"); assertTrue(topRootableTask.isTopStackInDisplayArea()); assertEquals(topRootableTask, - secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableStack); + secondaryDisplayActivity.getDisplayArea().mPreferredTopFocusableRootTask); // The global top focus activity is on secondary display now. // Finish top activity on default display and verify the next preferred top focusable stack @@ -870,7 +895,7 @@ public class ActivityRecordTests extends WindowTestsBase { topActivityOnNonTopDisplay.finishIfPossible(0 /* resultCode */, null /* resultData */, null /* resultGrants */, "test", false /* oomAdj */); assertEquals(task, task.getTopMostTask()); - assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableStack); + assertEquals(task, activity.getDisplayArea().mPreferredTopFocusableRootTask); } /** @@ -1275,7 +1300,7 @@ public class ActivityRecordTests extends WindowTestsBase { @Test public void testDestroyIfPossible() { final ActivityRecord activity = createActivityWithTask(); - doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); + doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities(); activity.destroyIfPossible("test"); assertEquals(DESTROYING, activity.getState()); @@ -1297,7 +1322,7 @@ public class ActivityRecordTests extends WindowTestsBase { homeStack.removeChild(t, "test"); }, true /* traverseTopToBottom */); activity.finishing = true; - doReturn(false).when(mRootWindowContainer).resumeFocusedStacksTopActivities(); + doReturn(false).when(mRootWindowContainer).resumeFocusedTasksTopActivities(); // Try to destroy the last activity above the home stack. activity.destroyIfPossible("test"); @@ -1658,7 +1683,7 @@ public class ActivityRecordTests extends WindowTestsBase { final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT ? SCREEN_ORIENTATION_LANDSCAPE : SCREEN_ORIENTATION_PORTRAIT; - doReturn(false).when(r).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(r).onDescendantOrientationChanged(any()); r.setOrientation(rotatedOrentation); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index 8ccbb8fe62ad..8e3e668804ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -48,7 +48,7 @@ import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; import static com.android.server.wm.Task.TASK_VISIBILITY_INVISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE; import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; -import static com.android.server.wm.TaskDisplayArea.getStackAbove; +import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -74,6 +74,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -154,7 +156,7 @@ public class ActivityStackTests extends WindowTestsBase { organizer.setMoveToSecondaryOnEnter(false); // Create primary splitscreen stack. - final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack( + final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Assert windowing mode. @@ -179,7 +181,7 @@ public class ActivityStackTests extends WindowTestsBase { public void testMoveToPrimarySplitScreenThenMoveToBack() { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); // This time, start with a fullscreen activitystack - final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack( + final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); primarySplitScreen.reparent(organizer.mPrimary, POSITION_TOP, @@ -205,11 +207,11 @@ public class ActivityStackTests extends WindowTestsBase { TestSplitOrganizer organizer = new TestSplitOrganizer(mAtm); // Set up split-screen with primary on top and secondary containing the home task below // another stack. - final Task primaryTask = mDefaultTaskDisplayArea.createStack( + final Task primaryTask = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task homeRoot = mDefaultTaskDisplayArea.getStack( + final Task homeRoot = mDefaultTaskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); - final Task secondaryTask = mDefaultTaskDisplayArea.createStack( + final Task secondaryTask = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary, false /* includingParents */); @@ -257,7 +259,7 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackInheritsDisplayWindowingMode() { - final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack( + final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); @@ -272,7 +274,7 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOverridesDisplayWindowingMode() { - final Task primarySplitScreen = mDefaultTaskDisplayArea.createStack( + final Task primarySplitScreen = mDefaultTaskDisplayArea.createRootTask( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode()); @@ -365,7 +367,7 @@ public class ActivityStackTests extends WindowTestsBase { verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */); // Also move display to back because there is only one stack left. - taskDisplayArea.removeStack(stack1); + taskDisplayArea.removeRootTask(stack1); stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.getTopMostTask()); verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */); } @@ -747,8 +749,8 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure that we don't move the home stack if it is already behind the top fullscreen stack int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); - assertEquals(fullscreenStack, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertEquals(fullscreenStack, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @@ -765,8 +767,8 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure that we don't move the home stack if it is already behind the top fullscreen stack int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); - assertEquals(fullscreenStack, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertEquals(fullscreenStack, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @@ -783,8 +785,8 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure we don't move the home stack if it is already on top int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); - assertNull(getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); + assertNull(getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @@ -807,9 +809,9 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the // pinned stack - assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); - assertEquals(fullscreenStack2, getStackAbove(homeStack)); + assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); + assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); } @Test @@ -830,9 +832,9 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure that we move the home stack behind the bottom most non-translucent fullscreen // stack - assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindBottomMostVisibleStack(homeStack); - assertEquals(fullscreenStack1, getStackAbove(homeStack)); + assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack); + assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); } @Test @@ -852,7 +854,7 @@ public class ActivityStackTests extends WindowTestsBase { // Ensure we don't move the home stack behind itself int homeStackIndex = mDefaultTaskDisplayArea.getIndexOf(homeStack); - mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, homeStack); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack); assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getIndexOf(homeStack)); } @@ -873,14 +875,14 @@ public class ActivityStackTests extends WindowTestsBase { final Task homeStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); - mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack1); - assertEquals(fullscreenStack1, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2); - assertEquals(fullscreenStack2, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack4); - assertEquals(fullscreenStack4, getStackAbove(homeStack)); - mDefaultTaskDisplayArea.moveStackBehindStack(homeStack, fullscreenStack2); - assertEquals(fullscreenStack2, getStackAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack1); + assertEquals(fullscreenStack1, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2); + assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack4); + assertEquals(fullscreenStack4, getRootTaskAbove(homeStack)); + mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, fullscreenStack2); + assertEquals(fullscreenStack2, getRootTaskAbove(homeStack)); } @Test @@ -889,7 +891,7 @@ public class ActivityStackTests extends WindowTestsBase { WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */); final Task pinnedStack = createStackForShouldBeVisibleTest(mDefaultTaskDisplayArea, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); - assertEquals(pinnedStack, getStackAbove(homeStack)); + assertEquals(pinnedStack, getRootTaskAbove(homeStack)); final Task alwaysOnTopStack = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, @@ -897,13 +899,13 @@ public class ActivityStackTests extends WindowTestsBase { alwaysOnTopStack.setAlwaysOnTop(true); assertTrue(alwaysOnTopStack.isAlwaysOnTop()); // Ensure (non-pinned) always on top stack is put below pinned stack. - assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack)); + assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack)); final Task nonAlwaysOnTopStack = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Ensure non always on top stack is put below always on top stacks. - assertEquals(alwaysOnTopStack, getStackAbove(nonAlwaysOnTopStack)); + assertEquals(alwaysOnTopStack, getRootTaskAbove(nonAlwaysOnTopStack)); final Task alwaysOnTopStack2 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, @@ -911,21 +913,21 @@ public class ActivityStackTests extends WindowTestsBase { alwaysOnTopStack2.setAlwaysOnTop(true); assertTrue(alwaysOnTopStack2.isAlwaysOnTop()); // Ensure newly created always on top stack is placed above other all always on top stacks. - assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack2)); + assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2)); alwaysOnTopStack2.setAlwaysOnTop(false); // Ensure, when always on top is turned off for a stack, the stack is put just below all // other always on top stacks. - assertEquals(alwaysOnTopStack, getStackAbove(alwaysOnTopStack2)); + assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2)); alwaysOnTopStack2.setAlwaysOnTop(true); // Ensure always on top state changes properly when windowing mode changes. alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FULLSCREEN); assertFalse(alwaysOnTopStack2.isAlwaysOnTop()); - assertEquals(alwaysOnTopStack, getStackAbove(alwaysOnTopStack2)); + assertEquals(alwaysOnTopStack, getRootTaskAbove(alwaysOnTopStack2)); alwaysOnTopStack2.setWindowingMode(WINDOWING_MODE_FREEFORM); assertTrue(alwaysOnTopStack2.isAlwaysOnTop()); - assertEquals(pinnedStack, getStackAbove(alwaysOnTopStack2)); + assertEquals(pinnedStack, getRootTaskAbove(alwaysOnTopStack2)); } @Test @@ -980,7 +982,8 @@ public class ActivityStackTests extends WindowTestsBase { int windowingMode, int activityType, boolean onTop, boolean twoLevelTask) { final Task task; if (activityType == ACTIVITY_TYPE_HOME) { - task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + task = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, + ACTIVITY_TYPE_HOME); mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task, false /* includingParents */); } else if (twoLevelTask) { @@ -1224,12 +1227,12 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnRemoveStack() { final Task task = new TaskBuilder(mSupervisor).build(); - StackOrderChangedListener listener = new StackOrderChangedListener(); - mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); + RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); + mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); try { - mDefaultTaskDisplayArea.removeStack(task); + mDefaultTaskDisplayArea.removeRootTask(task); } finally { - mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); } assertTrue(listener.mChanged); } @@ -1237,31 +1240,31 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testStackOrderChangedOnAddPositionStack() { final Task task = new TaskBuilder(mSupervisor).build(); - mDefaultTaskDisplayArea.removeStack(task); + mDefaultTaskDisplayArea.removeRootTask(task); - StackOrderChangedListener listener = new StackOrderChangedListener(); - mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); + RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); + mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); try { task.mReparenting = true; mDefaultTaskDisplayArea.addChild(task, 0); } finally { - mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); } assertTrue(listener.mChanged); } @Test public void testStackOrderChangedOnPositionStack() { - StackOrderChangedListener listener = new StackOrderChangedListener(); + RootTaskOrderChangedListener listener = new RootTaskOrderChangedListener(); try { final Task fullscreenStack1 = createStackForShouldBeVisibleTest( mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(listener); mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1, false /*includingParents*/); } finally { - mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener); + mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(listener); } assertTrue(listener.mChanged); } @@ -1447,12 +1450,12 @@ public class ActivityStackTests extends WindowTestsBase { assertEquals(expected, task.shouldSleepActivities()); } - private static class StackOrderChangedListener - implements TaskDisplayArea.OnStackOrderChangedListener { + private static class RootTaskOrderChangedListener + implements OnRootTaskOrderChangedListener { public boolean mChanged = false; @Override - public void onStackOrderChanged(Task stack) { + public void onRootTaskOrderChanged(Task rootTask) { mChanged = true; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java index c799f2902547..ce96771c8c27 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartControllerTests.java @@ -33,8 +33,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch; import com.android.server.wm.ActivityStarter.Factory; +import com.android.server.wm.ActivityTaskSupervisor.PendingActivityLaunch; import org.junit.Before; import org.junit.Test; @@ -77,8 +77,8 @@ public class ActivityStartControllerTests extends WindowTestsBase { .setCreateTask(true) .build(); final int startFlags = random.nextInt(); - final Task stack = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task rootTask = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final WindowProcessController wpc = new WindowProcessController(mAtm, mAtm.mContext.getApplicationInfo(), "name", 12345, UserHandle.getUserId(12345), mock(Object.class), @@ -86,7 +86,7 @@ public class ActivityStartControllerTests extends WindowTestsBase { wpc.setThread(mock(IApplicationThread.class)); mController.addPendingActivityLaunch( - new PendingActivityLaunch(activity, source, startFlags, stack, wpc, null)); + new PendingActivityLaunch(activity, source, startFlags, rootTask, wpc, null)); final boolean resume = random.nextBoolean(); mController.doPendingActivityLaunches(resume); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 565bf8b615c7..ef2e88913914 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -326,7 +326,7 @@ public class ActivityStarterTests extends WindowTestsBase { * Creates a {@link ActivityStarter} with default parameters and necessary mocks. * * @param launchFlags The intent flags to launch activity. - * @param mockGetLaunchStack Whether to mock {@link RootWindowContainer#getLaunchStack} for + * @param mockGetLaunchStack Whether to mock {@link RootWindowContainer#getLaunchRootTask} for * always launching to the testing stack. Set to false when allowing * the activity can be launched to any stack that is decided by real * implementation. @@ -342,14 +342,14 @@ public class ActivityStarterTests extends WindowTestsBase { if (mockGetLaunchStack) { // Instrument the stack and task used. final Task stack = mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Direct starter to use spy stack. doReturn(stack).when(mRootWindowContainer) - .getLaunchStack(any(), any(), any(), anyBoolean()); - doReturn(stack).when(mRootWindowContainer) - .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); + .getLaunchRootTask(any(), any(), any(), anyBoolean()); + doReturn(stack).when(mRootWindowContainer).getLaunchRootTask(any(), any(), any(), + anyBoolean(), any(), anyInt(), anyInt()); } // Set up mock package manager internal and make sure no unmocked methods are called @@ -513,8 +513,8 @@ public class ActivityStarterTests extends WindowTestsBase { private void assertNoTasks(DisplayContent display) { display.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); assertFalse(stack.hasChild()); } }); @@ -806,7 +806,7 @@ public class ActivityStarterTests extends WindowTestsBase { new TestDisplayContent.Builder(mAtm, 1000, 1500) .setPosition(POSITION_BOTTOM).build(); final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); - final Task stack = secondaryTaskContainer.createStack( + final Task stack = secondaryTaskContainer.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Create an activity record on the top of secondary display. @@ -828,7 +828,7 @@ public class ActivityStarterTests extends WindowTestsBase { assertEquals(START_DELIVERED_TO_TOP, result); // Ensure secondary display only creates one stack. - verify(secondaryTaskContainer, times(1)).createStack(anyInt(), anyInt(), anyBoolean()); + verify(secondaryTaskContainer, times(1)).createRootTask(anyInt(), anyInt(), anyBoolean()); } /** @@ -848,11 +848,11 @@ public class ActivityStarterTests extends WindowTestsBase { false /* includingParents */); final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); final ActivityRecord singleTaskActivity = createSingleTaskActivityOn( - secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, + secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); // Create another activity on top of the secondary display. - final Task topStack = secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, + final Task topStack = secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build(); new ActivityBuilder(mAtm).setTask(topTask).build(); @@ -870,7 +870,7 @@ public class ActivityStarterTests extends WindowTestsBase { assertEquals(START_TASK_TO_FRONT, result); // Ensure secondary display only creates two stacks. - verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean()); + verify(secondaryTaskContainer, times(2)).createRootTask(anyInt(), anyInt(), anyBoolean()); // The metrics logger should receive the same result and non-null options. verify(mActivityMetricsLogger).notifyActivityLaunched(any() /* launchingState */, eq(result), eq(singleTaskActivity), notNull() /* options */); @@ -938,7 +938,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Create a secondary display at bottom. final TestDisplayContent secondaryDisplay = addNewDisplayContentAt(POSITION_BOTTOM); final TaskDisplayArea secondaryTaskContainer = secondaryDisplay.getDefaultTaskDisplayArea(); - secondaryTaskContainer.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, + secondaryTaskContainer.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Put an activity on default display as the top focused activity. @@ -1053,7 +1053,7 @@ public class ActivityStarterTests extends WindowTestsBase { final ActivityStarter starter = prepareStarter(0 /* flags */); starter.mStartActivity = new ActivityBuilder(mAtm).build(); final Task task = new TaskBuilder(mAtm.mTaskSupervisor) - .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createStack( + .setParentTask(mAtm.mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */)) .setUserId(10) .build(); @@ -1117,13 +1117,13 @@ public class ActivityStarterTests extends WindowTestsBase { final Task stack = spy( mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, /* onTop */true)); stack.addChild(targetRecord); doReturn(stack).when(mRootWindowContainer) - .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); + .getLaunchRootTask(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt()); starter.mStartActivity = new ActivityBuilder(mAtm).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 6ca69bf974d5..8cc515e83342 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -196,7 +196,7 @@ public class AppTransitionTests extends WindowTestsBase { @Test public void testCancelRemoteAnimationWhenFreeze() { final DisplayContent dc = createNewDisplay(Display.STATE_ON); - doReturn(false).when(dc).onDescendantOrientationChanged(any(), any()); + doReturn(false).when(dc).onDescendantOrientationChanged(any()); final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "exiting app"); final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord; diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java index 5828d02948a1..59b12e406d70 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java @@ -366,6 +366,30 @@ public class ConfigurationContainerTests { assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); } + @Test + public void testOnRequestedOverrideConfigurationChangedOverrideMaxBounds() { + final TestConfigurationContainer root = + new TestConfigurationContainer(true /* providesMaxBounds */); + final Rect bounds = new Rect(0, 0, 10, 10); + final TestConfigurationContainer child = new TestConfigurationContainer(); + root.addChild(child); + final Configuration configuration = new Configuration(); + configuration.windowConfiguration.setBounds(bounds); + + root.onRequestedOverrideConfigurationChanged(configuration); + + assertEquals(bounds, root.getBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); + assertEquals(bounds, child.getBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); + + assertEquals(bounds, root.getMaxBounds()); + assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds()); + assertEquals(bounds, child.getMaxBounds()); + assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds()); + } + + /** * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed * for testing. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java index 06a6882dc698..bc91c709aeb1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java @@ -87,7 +87,7 @@ public class DisplayAreaGroupTest extends WindowTestsBase { final Task task = new TaskBuilder(mSupervisor) .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build(); final ActivityRecord activity = task.getTopNonFinishingActivity(); - doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any()); + doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any()); activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); // Display is portrait, DisplayAreaGroup inherits that diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java index 39bf8eb857b0..5a47493c12cd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyTests.java @@ -94,9 +94,9 @@ public class DisplayAreaPolicyTests { @Test public void testTaskDisplayArea_taskPositionChanged_updatesTaskDisplayAreaPosition() { - final Task stack1 = mTaskDisplayArea1.createStack( + final Task stack1 = mTaskDisplayArea1.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task stack2 = mTaskDisplayArea2.createStack( + final Task stack2 = mTaskDisplayArea2.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Initial order @@ -155,11 +155,11 @@ public class DisplayAreaPolicyTests { .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(group2) .setTaskDisplayAreas(Lists.newArrayList(taskDisplayArea5))) .build(wms); - final Task stack1 = taskDisplayArea1.createStack( + final Task stack1 = taskDisplayArea1.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task stack3 = taskDisplayArea3.createStack( + final Task stack3 = taskDisplayArea3.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task stack4 = taskDisplayArea4.createStack( + final Task stack4 = taskDisplayArea4.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Initial order diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index 59b2d4fc282b..025c5a6bb180 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -468,14 +468,14 @@ public class DisplayAreaTest extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); - verify(tda).onDescendantOrientationChanged(any(), any()); - verify(mDisplayContent, never()).onDescendantOrientationChanged(any(), any()); + verify(tda).onDescendantOrientationChanged(any()); + verify(mDisplayContent, never()).onDescendantOrientationChanged(any()); tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); - verify(tda, times(2)).onDescendantOrientationChanged(any(), any()); - verify(mDisplayContent).onDescendantOrientationChanged(any(), any()); + verify(tda, times(2)).onDescendantOrientationChanged(any()); + verify(mDisplayContent).onDescendantOrientationChanged(any()); } private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index d9217188582f..2053bfb45884 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -839,12 +839,12 @@ public class DisplayContentTests extends WindowTestsBase { final DisplayContent newDisplay = createNewDisplay(); final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); - final Task stack = mDisplayContent.getTopStack(); + final Task stack = mDisplayContent.getTopRootTask(); final ActivityRecord activity = stack.topRunningActivity(); doReturn(true).when(activity).shouldBeVisibleUnchecked(); final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); - final Task stack1 = newDisplay.getTopStack(); + final Task stack1 = newDisplay.getTopRootTask(); final ActivityRecord activity1 = stack1.topRunningActivity(); doReturn(true).when(activity1).shouldBeVisibleUnchecked(); appWin.setHasSurface(true); @@ -886,7 +886,7 @@ public class DisplayContentTests extends WindowTestsBase { doReturn(true).when(freeformStack).isVisible(); freeformStack.getTopChild().setBounds(100, 100, 300, 400); - assertTrue(dc.getDefaultTaskDisplayArea().isStackVisible(WINDOWING_MODE_FREEFORM)); + assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM)); freeformStack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); stack.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); @@ -1202,6 +1202,17 @@ public class DisplayContentTests extends WindowTestsBase { assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM)); + // If the visibility of insets state is changed, the rotated state should be updated too. + final InsetsState rotatedState = app.getFixedRotationTransformInsetsState(); + final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); + assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(), + rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + state.getSource(ITYPE_STATUS_BAR).setVisible( + !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + mDisplayContent.getInsetsStateController().notifyInsetsChanged(); + assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(), + rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); + final Rect outFrame = new Rect(); final Rect outInsets = new Rect(); final Rect outStableInsets = new Rect(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 21bdc9e7785e..79b2da187680 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -236,8 +236,7 @@ public class DisplayPolicyTests extends WindowTestsBase { final WindowState activity = createBaseApplicationWindow(); activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; - policy.adjustWindowParamsLw(activity, activity.mAttrs, 0 /* callingPid */, - 0 /* callingUid */); + policy.adjustWindowParamsLw(activity, activity.mAttrs); } private WindowState createApplicationWindow() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 94ffcdab4fa7..20775e84fd8f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -107,11 +107,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { } void addWindow(WindowState win) { - final int callingPid = Binder.getCallingPid(); - final int callingUid = Binder.getCallingUid(); - mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); - assertEquals(WindowManagerGlobal.ADD_OKAY, - mDisplayPolicy.validateAddingWindowLw(win.mAttrs, callingPid, callingUid)); + mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs); + assertEquals(WindowManagerGlobal.ADD_OKAY, mDisplayPolicy.validateAddingWindowLw( + win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid())); mDisplayPolicy.addWindowLw(win, win.mAttrs); win.mHasSurface = true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index eaedd58001c1..7c4f7db48022 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -16,7 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -27,6 +29,7 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; +import android.util.TypedXmlPullParser; import android.util.Xml; import android.view.Display; import android.view.DisplayAddress; @@ -67,7 +70,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { private static final File TEST_FOLDER = getInstrumentation().getTargetContext().getCacheDir(); - private TestStorage mBaseSettingsStorage; + private TestStorage mDefaultVendorSettingsStorage; + private TestStorage mSecondaryVendorSettingsStorage; private TestStorage mOverrideSettingsStorage; private DisplayContent mPrimaryDisplay; @@ -77,7 +81,8 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { public void setUp() throws Exception { deleteRecursively(TEST_FOLDER); - mBaseSettingsStorage = new TestStorage(); + mDefaultVendorSettingsStorage = new TestStorage(); + mSecondaryVendorSettingsStorage = new TestStorage(); mOverrideSettingsStorage = new TestStorage(); mPrimaryDisplay = mWm.getDefaultDisplayContentLocked(); @@ -120,7 +125,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Update settings with new value, should trigger write to injector. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(mPrimaryDisplayInfo); overrideSettings.mForcedDensity = 200; provider.updateOverrideSettings(mPrimaryDisplayInfo, overrideSettings); @@ -160,12 +165,55 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test + public void testReadingDisplaySettingsFromStorage_secondayVendorDisplaySettingsLocation() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + + // Expected settings should be empty because the default is to read from the primary vendor + // settings location. + SettingsEntry expectedSettings = new SettingsEntry(); + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Now switch to secondary vendor settings and assert proper settings. + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_FULLSCREEN; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + + // Switch back to primary and assert settings are empty again. + provider.setBaseSettingsStorage(mDefaultVendorSettingsStorage); + expectedSettings.mWindowingMode = WINDOWING_MODE_UNDEFINED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test + public void testReadingDisplaySettingsFromStorage_overrideSettingsTakePrecedenceOverVendor() { + final String displayIdentifier = mSecondaryDisplay.getDisplayInfo().uniqueId; + prepareOverrideDisplaySettings(displayIdentifier); + prepareSecondaryDisplaySettings(displayIdentifier); + + final DisplayWindowSettingsProvider provider = + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); + provider.setBaseSettingsStorage(mSecondaryVendorSettingsStorage); + + // The windowing mode should be set to WINDOWING_MODE_PINNED because the override settings + // take precedence over the vendor provided settings. + SettingsEntry expectedSettings = new SettingsEntry(); + expectedSettings.mWindowingMode = WINDOWING_MODE_PINNED; + assertEquals(expectedSettings, provider.getSettings(mPrimaryDisplay.getDisplayInfo())); + } + + @Test public void testWritingDisplaySettingsToStorage() throws Exception { final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -193,7 +241,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { // Write some settings to storage. DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( - mBaseSettingsStorage, mOverrideSettingsStorage); + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); overrideSettings.mShouldShowSystemDecors = true; overrideSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; @@ -234,10 +282,29 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { mOverrideSettingsStorage.setReadStream(is); } + /** + * Prepares display settings and stores in {@link #mSecondaryVendorSettingsStorage}. Uses + * provided display identifier and stores windowingMode=WINDOWING_MODE_FULLSCREEN. + */ + private void prepareSecondaryDisplaySettings(String displayIdentifier) { + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-settings>\n"; + if (displayIdentifier != null) { + contents += " <display\n" + + " name=\"" + displayIdentifier + "\"\n" + + " windowingMode=\"" + WINDOWING_MODE_FULLSCREEN + "\"/>\n"; + } + contents += "</display-settings>\n"; + + final InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mSecondaryVendorSettingsStorage.setReadStream(is); + } + private void readAndAssertExpectedSettings(DisplayContent displayContent, SettingsEntry expectedSettings) { final DisplayWindowSettingsProvider provider = - new DisplayWindowSettingsProvider(mBaseSettingsStorage, mOverrideSettingsStorage); + new DisplayWindowSettingsProvider(mDefaultVendorSettingsStorage, + mOverrideSettingsStorage); assertEquals(expectedSettings, provider.getSettings(displayContent.getDisplayInfo())); } @@ -245,7 +312,7 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { private String getStoredDisplayAttributeValue(TestStorage storage, String attr) throws Exception { try (InputStream stream = storage.openRead()) { - XmlPullParser parser = Xml.newPullParser(); + TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(stream, StandardCharsets.UTF_8.name()); int type; while ((type = parser.next()) != XmlPullParser.START_TAG diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java index c3e1922a09cc..dfc2e35ffedf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java @@ -31,7 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.util.Preconditions; -import com.android.server.wm.utils.FakeDeviceConfigInterface; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.After; import org.junit.Test; diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java index cd428e10a437..a99e40cc19ec 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java @@ -294,11 +294,11 @@ public class LaunchParamsControllerTests extends WindowTestsBase { mController.registerModifier(positioner); - doNothing().when(mRootWindowContainer).moveStackToTaskDisplayArea(anyInt(), any(), + doNothing().when(mRootWindowContainer).moveRootTaskToTaskDisplayArea(anyInt(), any(), anyBoolean()); mController.layoutTask(task, null /* windowLayout */); - verify(mRootWindowContainer, times(1)).moveStackToTaskDisplayArea(eq(task.getRootTaskId()), - eq(preferredTaskDisplayArea), anyBoolean()); + verify(mRootWindowContainer, times(1)).moveRootTaskToTaskDisplayArea( + eq(task.getRootTaskId()), eq(preferredTaskDisplayArea), anyBoolean()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java index d701a9df7cb8..b7d8638f06a8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java @@ -115,7 +115,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { .thenReturn(mTestDisplay); Task stack = mTestDisplay.getDefaultTaskDisplayArea() - .createStack(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); + .createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); mTestTask = new TaskBuilder(mSupervisor).setComponent(TEST_COMPONENT).setParentTask(stack) .build(); mTestTask.mUserId = TEST_USER_ID; @@ -337,7 +337,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { public void testClearsRecordsOfTheUserOnUserCleanUp() { mTarget.saveTask(mTestTask); - Task stack = mTestDisplay.getDefaultTaskDisplayArea().createStack( + Task stack = mTestDisplay.getDefaultTaskDisplayArea().createRootTask( TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); final Task anotherTaskOfTheSameUser = new TaskBuilder(mSupervisor) .setComponent(ALTERNATIVE_COMPONENT) @@ -349,7 +349,7 @@ public class LaunchParamsPersisterTests extends WindowTestsBase { anotherTaskOfTheSameUser.setHasBeenVisible(true); mTarget.saveTask(anotherTaskOfTheSameUser); - stack = mTestDisplay.getDefaultTaskDisplayArea().createStack(TEST_WINDOWING_MODE, + stack = mTestDisplay.getDefaultTaskDisplayArea().createRootTask(TEST_WINDOWING_MODE, ACTIVITY_TYPE_STANDARD, /* onTop */ true); final Task anotherTaskOfDifferentUser = new TaskBuilder(mSupervisor) .setComponent(TEST_COMPONENT) diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 7812934bb52f..4892ef3b6322 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -724,7 +724,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task alwaysOnTopTask = taskDisplayArea.createStack(WINDOWING_MODE_MULTI_WINDOW, + final Task alwaysOnTopTask = taskDisplayArea.createRootTask(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, true /* onTop */); alwaysOnTopTask.setAlwaysOnTop(true); @@ -838,7 +838,7 @@ public class RecentTasksTest extends WindowTestsBase { Task stack = mTasks.get(2).getRootTask(); stack.moveToFront("", mTasks.get(2)); - doReturn(stack).when(mAtm.mRootWindowContainer).getTopDisplayFocusedStack(); + doReturn(stack).when(mAtm.mRootWindowContainer).getTopDisplayFocusedRootTask(); // Simulate the reset from the timeout mRecentTasks.resetFreezeTaskListReorderingOnTimeout(); @@ -858,7 +858,7 @@ public class RecentTasksTest extends WindowTestsBase { mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); final Task homeStack = mTaskContainer.getRootHomeTask(); - final Task aboveHomeStack = mTaskContainer.createStack( + final Task aboveHomeStack = mTaskContainer.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all @@ -875,10 +875,10 @@ public class RecentTasksTest extends WindowTestsBase { public void testBehindHomeStackTasks_expectTaskTrimmed() { mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); - final Task behindHomeStack = mTaskContainer.createStack( + final Task behindHomeStack = mTaskContainer.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task homeStack = mTaskContainer.getRootHomeTask(); - final Task aboveHomeStack = mTaskContainer.createStack( + final Task aboveHomeStack = mTaskContainer.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind @@ -897,17 +897,17 @@ public class RecentTasksTest extends WindowTestsBase { public void testOtherDisplayTasks_expectNoTrim() { mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); - final Task homeStack = mTaskContainer.getRootHomeTask(); + final Task homeTask = mTaskContainer.getRootHomeTask(); final DisplayContent otherDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP); - final Task otherDisplayStack = otherDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task otherDisplayRootTask = otherDisplay.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not // removed - mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeStack).build()); - mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayStack).build()); - mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayStack).build()); - mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeStack).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask1").setParentTask(homeTask).build()); + mRecentTasks.add(createTaskBuilder(".Task1").setParentTask(otherDisplayRootTask).build()); + mRecentTasks.add(createTaskBuilder(".Task2").setParentTask(otherDisplayRootTask).build()); + mRecentTasks.add(createTaskBuilder(".HomeTask2").setParentTask(homeTask).build()); triggerTrimAndAssertNoTasksTrimmed(); } @@ -1103,9 +1103,9 @@ public class RecentTasksTest extends WindowTestsBase { private void assertNotRestoreTask(Runnable action) { // Verify stack count doesn't change because task with fullscreen mode and standard type // would have its own stack. - final int originalStackCount = mTaskContainer.getStackCount(); + final int originalStackCount = mTaskContainer.getRootTaskCount(); action.run(); - assertEquals(originalStackCount, mTaskContainer.getStackCount()); + assertEquals(originalStackCount, mTaskContainer.getRootTaskCount()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 40f73b12f805..5c39bd0a316b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -322,7 +322,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertEquals(landActivity.findMainWindow(), win1); // Ensure that the display is in Landscape - landActivity.onDescendantOrientationChanged(landActivity.token, landActivity); + landActivity.onDescendantOrientationChanged(landActivity); assertEquals(Configuration.ORIENTATION_LANDSCAPE, mDefaultDisplay.getConfiguration().orientation); diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 5ff3ff5a6e0c..63ee5d05fada 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -89,7 +89,7 @@ public class RecentsAnimationTest extends WindowTestsBase { @Test public void testRecentsActivityVisiblility() { TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentActivity = new ActivityBuilder(mAtm) .setComponent(mRecentsComponent) @@ -118,7 +118,7 @@ public class RecentsAnimationTest extends WindowTestsBase { public void testPreloadRecentsActivity() { TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); final Task homeStack = - defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); + defaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack, false /* includingParents */); ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity(); @@ -150,7 +150,7 @@ public class RecentsAnimationTest extends WindowTestsBase { mAtm.startRecentsActivity(recentsIntent, 0 /* eventTime */, null /* recentsAnimationRunner */); - Task recentsStack = defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, + Task recentsStack = defaultTaskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS); assertThat(recentsStack).isNotNull(); @@ -179,7 +179,7 @@ public class RecentsAnimationTest extends WindowTestsBase { public void testRestartRecentsActivity() throws Exception { // Have a recents activity that is not attached to its process (ActivityRecord.app = null). TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - Task recentsStack = defaultTaskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task recentsStack = defaultTaskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); ActivityRecord recentActivity = new ActivityBuilder(mAtm).setComponent( mRecentsComponent).setCreateTask(true).setParentTask(recentsStack).build(); @@ -251,21 +251,21 @@ public class RecentsAnimationTest extends WindowTestsBase { @Test public void testCancelAnimationOnVisibleStackOrderChange() { TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(new ComponentName(mContext.getPackageName(), "App1")) .setCreateTask(true) .setParentTask(fullscreenStack) .build(); - Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(mRecentsComponent) .setCreateTask(true) .setParentTask(recentsStack) .build(); - Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(new ComponentName(mContext.getPackageName(), "App2")) @@ -292,21 +292,21 @@ public class RecentsAnimationTest extends WindowTestsBase { @Test public void testKeepAnimationOnHiddenStackOrderChange() { TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(new ComponentName(mContext.getPackageName(), "App1")) .setCreateTask(true) .setParentTask(fullscreenStack) .build(); - Task recentsStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task recentsStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(mRecentsComponent) .setCreateTask(true) .setParentTask(recentsStack) .build(); - Task fullscreenStack2 = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task fullscreenStack2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(new ComponentName(mContext.getPackageName(), "App2")) @@ -329,7 +329,7 @@ public class RecentsAnimationTest extends WindowTestsBase { public void testMultipleUserHomeActivity_findUserHomeTask() { TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultDisplay() .getDefaultTaskDisplayArea(); - Task homeStack = taskDisplayArea.getStack(WINDOWING_MODE_UNDEFINED, + Task homeStack = taskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME); ActivityRecord otherUserHomeActivity = new ActivityBuilder(mAtm) .setParentTask(homeStack) @@ -338,7 +338,7 @@ public class RecentsAnimationTest extends WindowTestsBase { .build(); otherUserHomeActivity.getTask().mUserId = TEST_USER_ID; - Task fullscreenStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task fullscreenStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); new ActivityBuilder(mAtm) .setComponent(new ComponentName(mContext.getPackageName(), "App1")) diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 7ca364cf096f..f79e4cc9fa10 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -94,7 +94,7 @@ public class RootActivityContainerTests extends WindowTestsBase { @Before public void setUp() throws Exception { - mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createStack( + mFullscreenStack = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); doNothing().when(mAtm).updateSleepIfNeededLocked(); } @@ -128,7 +128,7 @@ public class RootActivityContainerTests extends WindowTestsBase { ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity); // Move first activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedStack(firstActivity, "initialMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, "initialMove"); final TaskDisplayArea taskDisplayArea = mFullscreenStack.getDisplayArea(); Task pinnedStack = taskDisplayArea.getRootPinnedTask(); @@ -137,11 +137,11 @@ public class RootActivityContainerTests extends WindowTestsBase { ensureStackPlacement(mFullscreenStack, secondActivity); // Move second activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "secondMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "secondMove"); // Need to get stacks again as a new instance might have been created. pinnedStack = taskDisplayArea.getRootPinnedTask(); - mFullscreenStack = taskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, + mFullscreenStack = taskDisplayArea.getRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); // Ensure stacks have swapped tasks. ensureStackPlacement(pinnedStack, secondActivity); @@ -166,7 +166,7 @@ public class RootActivityContainerTests extends WindowTestsBase { // Move first activity to pinned stack. - mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove"); + mRootWindowContainer.moveActivityToPinnedRootTask(secondActivity, "initialMove"); assertTrue(firstActivity.mRequestForceTransition); } @@ -237,9 +237,9 @@ public class RootActivityContainerTests extends WindowTestsBase { doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt()); doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay(); - doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack(); + doReturn(isFocusedStack ? stack : null).when(display).getFocusedRootTask(); TaskDisplayArea defaultTaskDisplayArea = display.getDefaultTaskDisplayArea(); - doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedStack(); + doReturn(isFocusedStack ? stack : null).when(defaultTaskDisplayArea).getFocusedRootTask(); mRootWindowContainer.applySleepTokens(true); verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked(); verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked( @@ -282,19 +282,19 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testRemovingStackOnAppCrash() { final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer .getDefaultTaskDisplayArea(); - final int originalStackCount = defaultTaskDisplayArea.getStackCount(); - final Task stack = defaultTaskDisplayArea.createStack( + final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount(); + final Task stack = defaultTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build(); - assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount()); + assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount()); // Let's pretend that the app has crashed. firstActivity.app.setThread(null); mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); // Verify that the stack was removed. - assertEquals(originalStackCount, defaultTaskDisplayArea.getStackCount()); + assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount()); } /** @@ -305,34 +305,34 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testRemovingStackOnAppCrash_multipleDisplayAreas() { final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer .getDefaultTaskDisplayArea(); - final int originalStackCount = defaultTaskDisplayArea.getStackCount(); - final Task stack = defaultTaskDisplayArea.createStack( + final int originalStackCount = defaultTaskDisplayArea.getRootTaskCount(); + final Task stack = defaultTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(stack).build(); - assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getStackCount()); + assertEquals(originalStackCount + 1, defaultTaskDisplayArea.getRootTaskCount()); final DisplayContent dc = defaultTaskDisplayArea.getDisplayContent(); final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( dc, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task secondStack = secondTaskDisplayArea.createStack( + final Task secondStack = secondTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); new ActivityBuilder(mAtm).setTask(secondStack).setUseProcess(firstActivity.app).build(); - assertEquals(1, secondTaskDisplayArea.getStackCount()); + assertEquals(1, secondTaskDisplayArea.getRootTaskCount()); // Let's pretend that the app has crashed. firstActivity.app.setThread(null); mRootWindowContainer.finishTopCrashedActivities(firstActivity.app, "test"); // Verify that the stacks were removed. - assertEquals(originalStackCount, defaultTaskDisplayArea.getStackCount()); - assertEquals(0, secondTaskDisplayArea.getStackCount()); + assertEquals(originalStackCount, defaultTaskDisplayArea.getRootTaskCount()); + assertEquals(0, secondTaskDisplayArea.getRootTaskCount()); } @Test public void testFocusability() { final TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer .getDefaultTaskDisplayArea(); - final Task stack = defaultTaskDisplayArea.createStack( + final Task stack = defaultTaskDisplayArea.createRootTask( WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); @@ -345,7 +345,7 @@ public class RootActivityContainerTests extends WindowTestsBase { assertFalse(stack.isTopActivityFocusable()); assertFalse(activity.isFocusable()); - final Task pinnedStack = defaultTaskDisplayArea.createStack( + final Task pinnedStack = defaultTaskDisplayArea.createRootTask( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */); final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm) .setTask(pinnedStack).build(); @@ -371,7 +371,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() { // Create primary split-screen stack with a task and an activity. final Task primaryStack = mRootWindowContainer.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, + .createRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */); final Task task = new TaskBuilder(mSupervisor).setParentTask(primaryStack).build(); final ActivityRecord r = new ActivityBuilder(mAtm).setTask(task).build(); @@ -381,7 +381,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final ActivityOptions options = ActivityOptions.makeBasic(); options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); final Task result = - mRootWindowContainer.getLaunchStack(r, options, task, true /* onTop */); + mRootWindowContainer.getLaunchRootTask(r, options, task, true /* onTop */); // Assert that the primary stack is returned. assertEquals(primaryStack, result); @@ -410,7 +410,7 @@ public class RootActivityContainerTests extends WindowTestsBase { false); final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - verify(taskDisplayArea).moveHomeStackToFront(contains(reason)); + verify(taskDisplayArea).moveHomeRootTaskToFront(contains(reason)); } /** @@ -421,7 +421,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testFindTaskToMoveToFrontWhenRecentsOnOtherDisplay() { // Create stack/task on default display. final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task targetStack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task targetStack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final Task targetTask = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); @@ -429,7 +429,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final TestDisplayContent secondDisplay = addNewDisplayContentAt( DisplayContent.POSITION_TOP); final Task stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); + .createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS, true /* onTop */); final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); new ActivityBuilder(mAtm).setTask(task).build(); @@ -437,7 +437,7 @@ public class RootActivityContainerTests extends WindowTestsBase { mSupervisor.findTaskToMoveToFront(targetTask, 0, ActivityOptions.makeBasic(), reason, false); - verify(taskDisplayArea, never()).moveHomeStackToFront(contains(reason)); + verify(taskDisplayArea, never()).moveHomeRootTaskToFront(contains(reason)); } /** @@ -448,7 +448,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testResumeActivityWhenNonTopmostStackIsTopFocused() { // Create a stack at bottom. final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -457,10 +457,10 @@ public class RootActivityContainerTests extends WindowTestsBase { // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it // is the current top focused stack. assertFalse(targetStack.isTopStackInDisplayArea()); - doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); + doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); // Use the stack as target to resume. - mRootWindowContainer.resumeFocusedStacksTopActivities( + mRootWindowContainer.resumeFocusedTasksTopActivities( targetStack, activity, null /* targetOptions */); // Verify the target stack should resume its activity. @@ -477,14 +477,14 @@ public class RootActivityContainerTests extends WindowTestsBase { mFullscreenStack.removeIfPossible(); final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); taskDisplayArea.getRootHomeTask().removeIfPossible(); - taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); mAtm.setBooted(true); // Trigger resume on all displays - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); // Verify that home activity was started on the default display verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); @@ -499,14 +499,14 @@ public class RootActivityContainerTests extends WindowTestsBase { mFullscreenStack.removeIfPossible(); final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); taskDisplayArea.getRootHomeTask().removeIfPossible(); - taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); // Create an activity on secondary display. final TestDisplayContent secondDisplay = addNewDisplayContentAt( DisplayContent.POSITION_TOP); - final Task stack = secondDisplay.getDefaultTaskDisplayArea() - .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); - final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); + final Task rootTask = secondDisplay.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); new ActivityBuilder(mAtm).setTask(task).build(); doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); @@ -514,7 +514,7 @@ public class RootActivityContainerTests extends WindowTestsBase { mAtm.setBooted(true); // Trigger resume on all displays - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); // Verify that home activity was started on the default display verify(mRootWindowContainer).resumeHomeActivity(any(), any(), eq(taskDisplayArea)); @@ -528,7 +528,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testResumeActivityLingeringTransition() { // Create a stack at top. final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -538,7 +538,7 @@ public class RootActivityContainerTests extends WindowTestsBase { assertTrue(targetStack.isTopStackInDisplayArea()); // Use the stack as target to resume. - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); // Verify the lingering app transition is being executed because it's already resumed verify(targetStack, times(1)).executeAppTransition(any()); @@ -548,7 +548,7 @@ public class RootActivityContainerTests extends WindowTestsBase { public void testResumeActivityLingeringTransition_notExecuted() { // Create a stack at bottom. final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); - final Task targetStack = spy(taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + final Task targetStack = spy(taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */)); final Task task = new TaskBuilder(mSupervisor).setParentTask(targetStack).build(); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); @@ -557,10 +557,10 @@ public class RootActivityContainerTests extends WindowTestsBase { // Assume the stack is at the topmost position assertFalse(targetStack.isTopStackInDisplayArea()); - doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); + doReturn(targetStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); // Use the stack as target to resume. - mRootWindowContainer.resumeFocusedStacksTopActivities(); + mRootWindowContainer.resumeFocusedTasksTopActivities(); // Verify the lingering app transition is being executed because it's already resumed verify(targetStack, never()).executeAppTransition(any()); @@ -586,9 +586,9 @@ public class RootActivityContainerTests extends WindowTestsBase { mRootWindowContainer.startHomeOnAllDisplays(0, "testStartHome"); - assertTrue(mRootWindowContainer.getDefaultDisplay().getTopStack().isActivityTypeHome()); - assertNotNull(secondDisplay.getTopStack()); - assertTrue(secondDisplay.getTopStack().isActivityTypeHome()); + assertTrue(mRootWindowContainer.getDefaultDisplay().getTopRootTask().isActivityTypeHome()); + assertNotNull(secondDisplay.getTopRootTask()); + assertTrue(secondDisplay.getTopRootTask().isActivityTypeHome()); } /** @@ -840,7 +840,7 @@ public class RootActivityContainerTests extends WindowTestsBase { } /** - * Test that {@link RootWindowContainer#getLaunchStack} with the real caller id will get the + * Test that {@link RootWindowContainer#getLaunchRootTask} with the real caller id will get the * expected stack when requesting the activity launch on the secondary display. */ @Test @@ -861,7 +861,7 @@ public class RootActivityContainerTests extends WindowTestsBase { options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); doReturn(true).when(mSupervisor).canPlaceEntityOnDisplay(secondaryDisplay.mDisplayId, 300 /* test realCallerPid */, 300 /* test realCallerUid */, r.info); - final Task result = mRootWindowContainer.getLaunchStack(r, options, + final Task result = mRootWindowContainer.getLaunchRootTask(r, options, null /* task */, true /* onTop */, null, 300 /* test realCallerPid */, 300 /* test realCallerUid */); @@ -881,7 +881,7 @@ public class RootActivityContainerTests extends WindowTestsBase { final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); // Make sure the root task is valid and can be reused on default display. - final Task stack = mRootWindowContainer.getValidLaunchStackInTaskDisplayArea( + final Task stack = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea( mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, null /* options */, null /* launchParams */); assertEquals(task, stack); @@ -889,7 +889,7 @@ public class RootActivityContainerTests extends WindowTestsBase { @Test public void testSwitchUser_missingHomeRootTask() { - doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack(); + doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedRootTask(); final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); Task homeStack = taskDisplayArea.getRootHomeTask(); @@ -904,7 +904,7 @@ public class RootActivityContainerTests extends WindowTestsBase { mRootWindowContainer.switchUser(otherUser, null); assertNotNull(taskDisplayArea.getRootHomeTask()); - assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask()); + assertEquals(taskDisplayArea.getTopRootTask(), taskDisplayArea.getRootHomeTask()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 42193c8fce78..9af2b96f3f78 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -92,7 +92,7 @@ public class RootWindowContainerTests extends WindowTestsBase { public void testAllPausedActivitiesComplete() { DisplayContent displayContent = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); TaskDisplayArea taskDisplayArea = displayContent.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.getStackAt(0); + Task stack = taskDisplayArea.getRootTaskAt(0); ActivityRecord activity = createActivityRecord(displayContent); stack.mPausingActivity = activity; diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java index 95e344d5c184..6821d47d5b1a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java @@ -71,7 +71,7 @@ public class RunningTasksTest extends WindowTestsBase { final int numTasks = 10; int activeTime = 0; for (int i = 0; i < numTasks; i++) { - createTask(display.getDefaultTaskDisplayArea().getStackAt(i % numStacks), + createTask(display.getDefaultTaskDisplayArea().getRootTaskAt(i % numStacks), ".Task" + i, i, activeTime++, null); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java deleted file mode 100644 index 25ba6db38e05..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.graphics.Color.RED; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.Gravity.BOTTOM; -import static android.view.Gravity.LEFT; -import static android.view.Gravity.RIGHT; -import static android.view.Gravity.TOP; -import static android.view.InsetsState.ITYPE_CLIMATE_BAR; -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertEquals; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.app.Instrumentation; -import android.content.Context; -import android.content.Intent; -import android.graphics.PixelFormat; -import android.graphics.Point; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.media.ImageReader; -import android.os.Handler; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.Pair; -import android.view.Display; -import android.view.DisplayInfo; -import android.view.View; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.compatibility.common.util.SystemUtil; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.function.BooleanSupplier; - -/** - * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag. - * - * Build/Install/Run: - * atest WmTests:ScreenDecorWindowTests - */ -// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags. -// TODO: Test non-Activity windows. -@SmallTest -@Presubmit -public class ScreenDecorWindowTests { - - private final Context mContext = getInstrumentation().getTargetContext(); - private final Instrumentation mInstrumentation = getInstrumentation(); - - private WindowManager mWm; - private ArrayList<View> mWindows = new ArrayList<>(); - - private Activity mTestActivity; - private VirtualDisplay mDisplay; - private ImageReader mImageReader; - - private int mDecorThickness; - private int mHalfDecorThickness; - - @Before - public void setUp() { - final Pair<VirtualDisplay, ImageReader> result = createDisplay(); - mDisplay = result.first; - mImageReader = result.second; - final Display display = mDisplay.getDisplay(); - final Context dContext = mContext.createDisplayContext(display); - mWm = dContext.getSystemService(WindowManager.class); - mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId()); - final Point size = new Point(); - mDisplay.getDisplay().getRealSize(size); - mDecorThickness = Math.min(size.x, size.y) / 3; - mHalfDecorThickness = mDecorThickness / 2; - } - - @After - public void tearDown() { - while (!mWindows.isEmpty()) { - removeWindow(mWindows.get(0)); - } - finishActivity(mTestActivity); - mDisplay.release(); - mImageReader.close(); - } - - @Test - public void testScreenSides() { - // Decor on top - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // Decor at the bottom - updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness); - - // Decor to the left - updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness); - - // Decor to the right - updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0); - assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness); - } - - // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported. - // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon. - @Ignore - @Test - public void testMultipleDecors() { - // Test 2 decor windows on-top. - createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness); - createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - // And one at the bottom. - createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness); - } - - @Test - public void testFlagChange() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertTopInsetEquals(mTestActivity, mDecorThickness); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - 0, PRIVATE_FLAG_IS_SCREEN_DECOR); - - // TODO: fix test and re-enable assertion. - // initialInsets was not actually immutable and just updated to the current insets, - // meaning this assertion never actually tested anything. Now that WindowInsets actually is - // immutable, it turns out the test was broken. - // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - - updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness, - PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR); - assertTopInsetEquals(mTestActivity, mDecorThickness); - } - - @Test - public void testRemoval() { - WindowInsets initialInsets = getInsets(mTestActivity); - - final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness); - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - - removeWindow(decorWindow); - assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop()); - } - - @Test - public void testProvidesInsetsTypes() { - int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR}; - final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED, - FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes); - - assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness); - } - - private View createDecorWindow(int gravity, int width, int height) { - int[] providesInsetsTypes = - new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR}; - return createWindow("decorWindow", gravity, width, height, RED, - FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes); - } - - private View createWindow(String name, int gravity, int width, int height, int color, int flags, - int privateFlags, int[] providesInsetsTypes) { - - final View[] viewHolder = new View[1]; - final int finalFlag = flags - | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE; - - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE); - lp.gravity = gravity; - lp.privateFlags |= privateFlags; - lp.providesInsetsTypes = providesInsetsTypes; - - final TextView view = new TextView(mContext); - view.setText("ScreenDecorWindowTests - " + name); - view.setBackgroundColor(color); - mWm.addView(view, lp); - mWindows.add(view); - viewHolder[0] = view; - }, 0); - - waitForIdle(); - return viewHolder[0]; - } - - private void updateWindow(View v, int gravity, int width, int height, - int privateFlags, int privateFlagsMask) { - // Needs to run on the UI thread. - Handler.getMain().runWithScissors(() -> { - final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams(); - lp.gravity = gravity; - lp.width = width; - lp.height = height; - setPrivateFlags(lp, privateFlags, privateFlagsMask); - - mWm.updateViewLayout(v, lp); - }, 0); - - waitForIdle(); - } - - private void removeWindow(View v) { - Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0); - mWindows.remove(v); - waitForIdle(); - } - - private WindowInsets getInsets(Activity a) { - return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets()); - } - - /** - * Set the flags of the window, as per the - * {@link WindowManager.LayoutParams WindowManager.LayoutParams} - * flags. - * - * @param flags The new window flags (see WindowManager.LayoutParams). - * @param mask Which of the window flag bits to modify. - */ - public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) { - lp.flags = (lp.flags & ~mask) | (flags & mask); - } - - /** - * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed. - */ - private void assertTopInsetEquals(Activity activity, int expected) { - waitForTopInsetEqual(activity, expected); - assertEquals(expected, getInsets(activity).getSystemWindowInsetTop()); - } - - private void waitForTopInsetEqual(Activity activity, int expected) { - waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected); - } - - /** - * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected} - * waiting as needed. - */ - private void assertInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitForInsetGreaterOrEqual(activity, side, expected); - - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: - assertThat(insets.getSystemWindowInsetTop()).isAtLeast(expected); - break; - case BOTTOM: - assertThat(insets.getSystemWindowInsetBottom()).isAtLeast(expected); - break; - case LEFT: - assertThat(insets.getSystemWindowInsetLeft()).isAtLeast(expected); - break; - case RIGHT: - assertThat(insets.getSystemWindowInsetRight()).isAtLeast(expected); - break; - } - } - - private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) { - waitFor(() -> { - final WindowInsets insets = getInsets(activity); - switch (side) { - case TOP: return insets.getSystemWindowInsetTop() >= expected; - case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected; - case LEFT: return insets.getSystemWindowInsetLeft() >= expected; - case RIGHT: return insets.getSystemWindowInsetRight() >= expected; - default: return true; - } - }); - } - - private void waitFor(BooleanSupplier waitCondition) { - int retriesLeft = 5; - do { - if (waitCondition.getAsBoolean()) { - break; - } - SystemClock.sleep(500); - } while (retriesLeft-- > 0); - } - - private void finishActivity(Activity a) { - if (a == null) { - return; - } - a.finish(); - waitForIdle(); - } - - private void waitForIdle() { - mInstrumentation.waitForIdleSync(); - } - - private Activity startActivityOnDisplay(Class<?> cls, int displayId) { - final Intent intent = new Intent(mContext, cls); - intent.addFlags(FLAG_ACTIVITY_NEW_TASK); - final ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchDisplayId(displayId); - - final Activity activity = SystemUtil.runWithShellPermissionIdentity( - () -> mInstrumentation.startActivitySync(intent, options.toBundle()), - "android.permission.ACTIVITY_EMBEDDING"); - waitForIdle(); - - assertEquals(displayId, activity.getDisplayId()); - return activity; - } - - private Pair<VirtualDisplay, ImageReader> createDisplay() { - final DisplayManager dm = mContext.getSystemService(DisplayManager.class); - final DisplayInfo displayInfo = new DisplayInfo(); - final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY); - defaultDisplay.getDisplayInfo(displayInfo); - final String name = "ScreenDecorWindowTests"; - int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; - - final ImageReader imageReader = ImageReader.newInstance( - displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2); - - final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth, - displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(), - flags); - - return Pair.create(display, imageReader); - } - - public static class TestActivity extends Activity { - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index a4bf5948c6a3..da00198030e4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -698,7 +698,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. - verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); final Rect displayBounds = display.getBounds(); @@ -739,7 +739,7 @@ public class SizeCompatTests extends WindowTestsBase { // Update with new activity requested orientation and recompute bounds with no previous // size compat cache. - verify(mTask).onDescendantOrientationChanged(any(), same(newActivity)); + verify(mTask).onDescendantOrientationChanged(same(newActivity)); verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt()); final Rect displayBounds = display.getBounds(); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index 2db736e63391..83e3d22345e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -299,6 +299,7 @@ public class SystemServicesTestRule implements TestRule { doNothing().when(mWmService.mRoot).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); spyOn(mWmService.mDisplayWindowSettings); + spyOn(mWmService.mDisplayWindowSettingsProvider); // Setup factory classes to prevent calls to native code. mTransaction = spy(StubTransaction.class); @@ -326,7 +327,7 @@ public class SystemServicesTestRule implements TestRule { // Set the default focused TDA. display.setLastFocusedTaskDisplayArea(taskDisplayArea); spyOn(taskDisplayArea); - final Task homeStack = taskDisplayArea.getStack( + final Task homeStack = taskDisplayArea.getRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); spyOn(homeStack); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index 28ba710797c9..d80f81642474 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -181,33 +181,33 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent); final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); - doReturn(newStack).when(taskDisplayArea).createStack(anyInt(), anyInt(), anyBoolean(), + doReturn(newStack).when(taskDisplayArea).createRootTask(anyInt(), anyInt(), anyBoolean(), any(), any(), anyBoolean()); final int type = ACTIVITY_TYPE_STANDARD; - assertGetOrCreateStack(WINDOWING_MODE_FULLSCREEN, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask, true /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_UNDEFINED, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_UNDEFINED, type, candidateTask, true /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, type, candidateTask, true /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_FREEFORM, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_FREEFORM, type, candidateTask, true /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_MULTI_WINDOW, type, candidateTask, true /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, type, candidateTask, false /* reuseCandidate */); - assertGetOrCreateStack(WINDOWING_MODE_PINNED, type, candidateTask, + assertGetOrCreateRootTask(WINDOWING_MODE_PINNED, type, candidateTask, true /* reuseCandidate */); final int windowingMode = WINDOWING_MODE_FULLSCREEN; - assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_HOME, candidateTask, + assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_HOME, candidateTask, false /* reuseCandidate */); - assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask, + assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_RECENTS, candidateTask, false /* reuseCandidate */); - assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask, + assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_ASSISTANT, candidateTask, false /* reuseCandidate */); - assertGetOrCreateStack(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask, + assertGetOrCreateRootTask(windowingMode, ACTIVITY_TYPE_DREAM, candidateTask, false /* reuseCandidate */); } @@ -250,9 +250,9 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task firstStack = firstTaskDisplayArea.createStack( + final Task firstStack = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task secondStack = secondTaskDisplayArea.createStack( + final Task secondStack = secondTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm) .setTask(firstStack).build(); @@ -281,7 +281,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { @Test public void testIgnoreOrientationRequest() { final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createStack( + final Task stack = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); @@ -326,10 +326,10 @@ public class TaskDisplayAreaTests extends WindowTestsBase { assertFalse(defaultTaskDisplayArea.mChildren.contains(task)); } - private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask, + private void assertGetOrCreateRootTask(int windowingMode, int activityType, Task candidateTask, boolean reuseCandidate) { final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea(); - final Task stack = taskDisplayArea.getOrCreateStack(windowingMode, activityType, + final Task stack = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType, false /* onTop */, null /* intent */, candidateTask /* candidateTask */); assertEquals(reuseCandidate, stack == candidateTask); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 4f55322e2085..269ce5d833f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -1566,13 +1566,13 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { private ActivityRecord createSourceActivity(TestDisplayContent display) { final Task stack = display.getDefaultTaskDisplayArea() - .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); + .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); return new ActivityBuilder(mAtm).setTask(stack).build(); } private void addFreeformTaskTo(TestDisplayContent display, Rect bounds) { final Task stack = display.getDefaultTaskDisplayArea() - .createStack(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); + .createRootTask(display.getWindowingMode(), ACTIVITY_TYPE_STANDARD, true); stack.setWindowingMode(WINDOWING_MODE_FREEFORM); final Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); // Just work around the unnecessary adjustments for bounds. diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index d080fa0445ef..4a5ff58fca3c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -70,6 +70,8 @@ import android.graphics.Rect; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.util.DisplayMetrics; +import android.util.TypedXmlPullParser; +import android.util.TypedXmlSerializer; import android.util.Xml; import android.view.DisplayInfo; @@ -175,7 +177,7 @@ public class TaskRecordTests extends WindowTestsBase { public void testFitWithinBounds() { final Rect parentBounds = new Rect(10, 10, 200, 200); TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FREEFORM, + Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */); Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); final Configuration parentConfig = stack.getConfiguration(); @@ -495,7 +497,7 @@ public class TaskRecordTests extends WindowTestsBase { @Test public void testInsetDisregardedWhenFreeformOverlapsNavBar() { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createStack(WINDOWING_MODE_FULLSCREEN, + Task stack = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); DisplayInfo displayInfo = new DisplayInfo(); mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); @@ -1025,9 +1027,9 @@ public class TaskRecordTests extends WindowTestsBase { final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", FEATURE_VENDOR_FIRST); - final Task firstStack = firstTaskDisplayArea.createStack( + final Task firstStack = firstTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); - final Task secondStack = secondTaskDisplayArea.createStack( + final Task secondStack = secondTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord firstActivity = new ActivityBuilder(mAtm) .setTask(firstStack).build(); @@ -1064,12 +1066,12 @@ public class TaskRecordTests extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); - verify(display).onDescendantOrientationChanged(any(), same(task)); + verify(display).onDescendantOrientationChanged(same(task)); reset(display); display.setWindowingMode(WINDOWING_MODE_FULLSCREEN); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation()); - verify(display).onDescendantOrientationChanged(any(), same(task)); + verify(display).onDescendantOrientationChanged(same(task)); } private Task getTestTask() { @@ -1081,7 +1083,7 @@ public class TaskRecordTests extends WindowTestsBase { Rect expectedConfigBounds) { TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); - Task stack = taskDisplayArea.createStack(windowingMode, ACTIVITY_TYPE_STANDARD, + Task stack = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */); Task task = new TaskBuilder(mSupervisor).setParentTask(stack).build(); @@ -1097,7 +1099,7 @@ public class TaskRecordTests extends WindowTestsBase { private byte[] serializeToBytes(Task r) throws Exception { try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { - final XmlSerializer serializer = Xml.newSerializer(); + final TypedXmlSerializer serializer = Xml.newFastSerializer(); serializer.setOutput(os, "UTF-8"); serializer.startDocument(null, true); serializer.startTag(null, TASK_TAG); @@ -1112,7 +1114,7 @@ public class TaskRecordTests extends WindowTestsBase { private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException { try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) { - final XmlPullParser parser = Xml.newPullParser(); + final TypedXmlPullParser parser = Xml.newFastPullParser(); parser.setInput(reader); assertEquals(XmlPullParser.START_TAG, parser.next()); assertEquals(TASK_TAG, parser.getName()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 3d8adbd215bd..c4bcfdbcf89d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -56,7 +56,6 @@ import static org.junit.Assert.assertTrue; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.IRemoteAnimationFinishedCallback; @@ -787,12 +786,11 @@ public class WindowContainerTests extends WindowTestsBase { final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm); final TestWindowContainer root = spy(builder.build()); - final IBinder binder = mock(IBinder.class); final ActivityRecord activityRecord = mock(ActivityRecord.class); final TestWindowContainer child = root.addChildWindow(); - child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord); - verify(root).onDescendantOrientationChanged(binder, activityRecord); + child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activityRecord); + verify(root).onDescendantOrientationChanged(activityRecord); } @Test @@ -814,7 +812,7 @@ public class WindowContainerTests extends WindowTestsBase { final ActivityRecord activity = createActivityRecord(mDisplayContent, task); final DisplayContent newDc = createNewDisplay(); - stack.getDisplayArea().removeStack(stack); + stack.getDisplayArea().removeRootTask(stack); newDc.getDefaultTaskDisplayArea().addChild(stack, POSITION_TOP); verify(stack).onDisplayChanged(newDc); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java index 52100116df53..7a0ef0d7d7a9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java @@ -32,7 +32,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.server.wm.utils.FakeDeviceConfigInterface; +import com.android.server.testutils.FakeDeviceConfigInterface; import org.junit.After; import org.junit.Before; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java index ba144dd367d9..4e1b3510bdaa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java @@ -20,7 +20,7 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; -import static android.provider.Settings.Global.DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS; +import static android.provider.Settings.Global.DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -58,8 +58,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceDesktopModeOnExternalDisplays() { - try (SettingsSession forceDesktopModeSession = new - SettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { + try (BoolSettingsSession forceDesktopModeSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS)) { final boolean forceDesktopMode = !forceDesktopModeSession.getSetting(); final Uri forceDesktopModeUri = forceDesktopModeSession.setSetting(forceDesktopMode); mWm.mSettingsObserver.onChange(false, forceDesktopModeUri); @@ -70,8 +70,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -84,8 +84,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testFreeformWindow_valueChanged_updatesDisplaySettings() { - try (SettingsSession freeformWindowSession = new - SettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { + try (BoolSettingsSession freeformWindowSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT)) { final boolean curFreeformWindow = freeformWindowSession.getSetting(); final boolean newFreeformWindow = !curFreeformWindow; final Uri freeformWindowUri = freeformWindowSession.setSetting(newFreeformWindow); @@ -106,8 +106,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testForceResizableMode() { - try (SettingsSession forceResizableSession = new - SettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { + try (BoolSettingsSession forceResizableSession = new + BoolSettingsSession(DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES)) { final boolean forceResizableMode = !forceResizableSession.getSetting(); final Uri forceResizableUri = forceResizableSession.setSetting(forceResizableMode); @@ -119,8 +119,8 @@ public class WindowManagerSettingsTests extends WindowTestsBase { @Test public void testEnableSizeCompatFreeform() { - try (SettingsSession enableSizeCompatFreeformSession = new - SettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { + try (BoolSettingsSession enableSizeCompatFreeformSession = new + BoolSettingsSession(DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM)) { final boolean enableSizeCompatFreeform = !enableSizeCompatFreeformSession.getSetting(); final Uri enableSizeCompatFreeformUri = @@ -132,21 +132,22 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } @Test - public void testEnabledIgnoreVendorDisplaySettings() { - try (SettingsSession ignoreVendorDisplaySettingsSession = new - SettingsSession(DEVELOPMENT_IGNORE_VENDOR_DISPLAY_SETTINGS)) { - final boolean ignoreVendorDisplaySettings = - !ignoreVendorDisplaySettingsSession.getSetting(); - final Uri ignoreVendorDisplaySettingUri = - ignoreVendorDisplaySettingsSession.setSetting(ignoreVendorDisplaySettings); + public void testChangeBaseDisplaySettingsPath() { + try (StringSettingsSession baseDisplaySettingsPathSession = new + StringSettingsSession(DEVELOPMENT_WM_DISPLAY_SETTINGS_PATH)) { + final String path = baseDisplaySettingsPathSession.getSetting() + "-test"; + final Uri baseDisplaySettingsPathUri = baseDisplaySettingsPathSession.setSetting(path); clearInvocations(mWm.mRoot); clearInvocations(mWm.mDisplayWindowSettings); + clearInvocations(mWm.mDisplayWindowSettingsProvider); - mWm.mSettingsObserver.onChange(false /* selfChange */, ignoreVendorDisplaySettingUri); + mWm.mSettingsObserver.onChange(false /* selfChange */, baseDisplaySettingsPathUri); - assertEquals(mWm.mDisplayWindowSettingsProvider.getVendorSettingsIgnored(), - ignoreVendorDisplaySettings); + ArgumentCaptor<String> pathCaptor = ArgumentCaptor.forClass(String.class); + verify(mWm.mDisplayWindowSettingsProvider).setBaseSettingsFilePath( + pathCaptor.capture()); + assertEquals(path, pathCaptor.getValue()); ArgumentCaptor<DisplayContent> captor = ArgumentCaptor.forClass(DisplayContent.class); @@ -161,14 +162,14 @@ public class WindowManagerSettingsTests extends WindowTestsBase { } } - private class SettingsSession implements AutoCloseable { + private class BoolSettingsSession implements AutoCloseable { private static final int SETTING_VALUE_OFF = 0; private static final int SETTING_VALUE_ON = 1; private final String mSettingName; private final int mInitialValue; - SettingsSession(String name) { + BoolSettingsSession(String name) { mSettingName = name; mInitialValue = getSetting() ? SETTING_VALUE_ON : SETTING_VALUE_OFF; } @@ -192,4 +193,32 @@ public class WindowManagerSettingsTests extends WindowTestsBase { setSetting(mInitialValue == SETTING_VALUE_ON); } } + + private class StringSettingsSession implements AutoCloseable { + private final String mSettingName; + private final String mInitialValue; + + StringSettingsSession(String name) { + mSettingName = name; + mInitialValue = getSetting(); + } + + String getSetting() { + return Settings.Global.getString(getContentResolver(), mSettingName); + } + + Uri setSetting(String value) { + Settings.Global.putString(getContentResolver(), mSettingName, value); + return Settings.Global.getUriFor(mSettingName); + } + + private ContentResolver getContentResolver() { + return getInstrumentation().getTargetContext().getContentResolver(); + } + + @Override + public void close() { + setSetting(mInitialValue); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 8fe65eb2747d..dba157e6ce06 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -387,7 +387,7 @@ public class WindowOrganizerTests extends WindowTestsBase { public void testSetIgnoreOrientationRequest_taskDisplayArea() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createStack( + final Task stack = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); @@ -425,7 +425,7 @@ public class WindowOrganizerTests extends WindowTestsBase { public void testSetIgnoreOrientationRequest_displayContent() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); - final Task stack = taskDisplayArea.createStack( + final Task stack = taskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(stack).build(); mDisplayContent.setFocusedApp(activity); @@ -743,8 +743,8 @@ public class WindowOrganizerTests extends WindowTestsBase { private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) { ArrayList<Task> out = new ArrayList<>(); dc.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task t = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task t = taskDisplayArea.getRootTaskAt(sNdx); if (t.mCreatedByOrganizer) out.add(t); } }); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 6c046bda1444..14a62d1a4ff0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -27,6 +27,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.os.Process.SYSTEM_UID; import static android.view.View.VISIBLE; +import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; +import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -40,8 +42,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; -import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; -import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -64,7 +64,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.Build; import android.os.Bundle; @@ -797,7 +796,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mTask = new TaskBuilder(mService.mTaskSupervisor) .setComponent(mComponent) .setParentTask(mParentTask).build(); - } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateStack( + } else if (mTask == null && mParentTask != null && DisplayContent.alwaysCreateRootTask( mParentTask.getWindowingMode(), mParentTask.getActivityType())) { // The stack can be the task root. mTask = mParentTask; @@ -831,13 +830,14 @@ class WindowTestsBase extends SystemServiceTestsBase { if (mLaunchTaskBehind) { options = ActivityOptions.makeTaskLaunchBehind(); } + final ActivityRecord activity = new ActivityRecord.Builder(mService) + .setLaunchedFromPid(mLaunchedFromPid) + .setLaunchedFromUid(mLaunchedFromUid) + .setIntent(intent) + .setActivityInfo(aInfo) + .setActivityOptions(options) + .build(); - final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, - mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */, - null, null, intent, null, aInfo /*aInfo*/, new Configuration(), - null /* resultTo */, null /* resultWho */, 0 /* reqCode */, - false /*componentSpecified*/, false /* rootVoiceInteraction */, - mService.mTaskSupervisor, options, null /* sourceRecord */); spyOn(activity); if (mTask != null) { // fullscreen value is normally read from resources in ctor, so for testing we need @@ -871,7 +871,7 @@ class WindowTestsBase extends SystemServiceTestsBase { activity.processName, activity.info.applicationInfo.uid); // Resume top activities to make sure all other signals in the system are connected. - mService.mRootWindowContainer.resumeFocusedStacksTopActivities(); + mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); return activity; } } @@ -995,7 +995,7 @@ class WindowTestsBase extends SystemServiceTestsBase { // Create parent task. if (mParentTask == null && mCreateParentTask) { - mParentTask = mTaskDisplayArea.createStack( + mParentTask = mTaskDisplayArea.createRootTask( WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); } if (mParentTask != null && !Mockito.mockingDetails(mParentTask).isSpy()) { @@ -1020,9 +1020,9 @@ class WindowTestsBase extends SystemServiceTestsBase { } Task task; - final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextStackId(); + final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId(); if (mParentTask == null) { - task = mTaskDisplayArea.createStackUnchecked( + task = mTaskDisplayArea.createRootTaskUnchecked( mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent, false /* createdByOrganizer */, false /* deferTaskAppear */, null /* launchCookie */); @@ -1114,8 +1114,8 @@ class WindowTestsBase extends SystemServiceTestsBase { mSecondary.mRemoteToken.toWindowContainerToken()); DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId); dc.forAllTaskDisplayAreas(taskDisplayArea -> { - for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) { - final Task stack = taskDisplayArea.getStackAt(sNdx); + for (int sNdx = taskDisplayArea.getRootTaskCount() - 1; sNdx >= 0; --sNdx) { + final Task stack = taskDisplayArea.getRootTaskAt(sNdx); if (!WindowConfiguration.isSplitScreenWindowingMode(stack.getWindowingMode())) { stack.reparent(mSecondary, POSITION_BOTTOM); } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 3af88e1c6354..d585b2374783 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1213,9 +1213,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser Slog.d(TAG, "Clear notification"); mUsbNotificationId = 0; } - // Not relevant for automotive. - if (mContext.getPackageManager().hasSystemFeature( + // Not relevant for automotive and watch. + if ((mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE) + || mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_WATCH)) && id == SystemMessage.NOTE_USB_CHARGING) { mUsbNotificationId = 0; return; diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java index 6ae6d002d990..aeac36e3a5f5 100644 --- a/telephony/java/android/telephony/CallForwardingInfo.java +++ b/telephony/java/android/telephony/CallForwardingInfo.java @@ -86,7 +86,7 @@ public final class CallForwardingInfo implements Parcelable { * Call forwarding reason types * @hide */ - @IntDef(flag = true, prefix = { "REASON_" }, value = { + @IntDef(prefix = { "REASON_" }, value = { REASON_UNCONDITIONAL, REASON_BUSY, REASON_NO_REPLY, diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index dad18ffe4fa7..0939bf02a039 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -53,9 +53,13 @@ public class CarrierConfigManager { public static final String EXTRA_SLOT_INDEX = "android.telephony.extra.SLOT_INDEX"; /** - * Extra included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is a - * rebroadcast on unlock. Defaults to {@code false} if not specified. - * @hide + * {@link #ACTION_CARRIER_CONFIG_CHANGED} is broadcast once on device bootup and then again when + * the device is unlocked. Direct-Boot-aware applications may use the first broadcast as an + * early signal that the carrier config has been loaded, but other applications will only + * receive the second broadcast, when the device is unlocked. + * + * This extra is included in {@link #ACTION_CARRIER_CONFIG_CHANGED} to indicate whether this is + * a rebroadcast on unlock. */ public static final String EXTRA_REBROADCAST_ON_UNLOCK = "android.telephony.extra.REBROADCAST_ON_UNLOCK"; @@ -2029,8 +2033,16 @@ public class CarrierConfigManager { "allow_hold_call_during_emergency_bool"; /** - * Flag indicating whether the carrier supports RCS presence indication for - * User Capability Exchange (UCE). When presence is supported, the device should use the + * Flag indicating whether or not the carrier supports the periodic exchange of phone numbers + * in the user's address book with the carrier's presence server in order to retrieve the RCS + * capabilities for each contact used in the RCS User Capability Exchange (UCE) procedure. See + * RCC.71, section 3 for more information. + * <p> + * The flag {@link Ims#KEY_ENABLE_PRESENCE_PUBLISH_BOOL} must also be enabled if this flag is + * enabled, as sending a periodic SIP PUBLISH with this device's RCS capabilities is a + * requirement for capability exchange to begin. + * <p> + * When presence is supported, the device should use the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} bit mask and set the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit to indicate * whether each contact supports video calling. The UI is made aware that presence is enabled @@ -3846,12 +3858,27 @@ public class CarrierConfigManager { public static final String KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL = KEY_PREFIX + "ims_single_registration_required_bool"; + /** + * A boolean flag specifying whether or not this carrier supports the device notifying the + * network of its RCS capabilities using the SIP PUBLISH procedure defined for User + * Capability Exchange (UCE). See RCC.71, section 3 for more information. + * <p> + * If this key's value is set to false, the procedure for RCS contact capability exchange + * via SIP SUBSCRIBE/NOTIFY will also be disabled internally, and + * {@link #KEY_USE_RCS_PRESENCE_BOOL} must also be set to false to ensure apps do not + * improperly think that capability exchange via SIP PUBLISH is enabled. + * <p> The default value for this key is {@code false}. + */ + public static final String KEY_ENABLE_PRESENCE_PUBLISH_BOOL = + KEY_PREFIX + "enable_presence_publish_bool"; + private Ims() {} private static PersistableBundle getDefaults() { PersistableBundle defaults = new PersistableBundle(); defaults.putInt(KEY_WIFI_OFF_DEFERRING_TIME_MILLIS_INT, 4000); defaults.putBoolean(KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL, false); + defaults.putBoolean(KEY_ENABLE_PRESENCE_PUBLISH_BOOL, false); return defaults; } } @@ -3985,6 +4012,17 @@ public class CarrierConfigManager { "default_preferred_apn_name_string"; /** + * Indicates if the carrier supports call composer. + */ + public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool"; + + /** + * Indicates the carrier server url that serves the call composer picture. + */ + public static final String KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING = + "call_composer_picture_server_url_string"; + + /** * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values * for IPv4 and IPv6 if both are sent. * TODO: remove in later release @@ -4536,6 +4574,8 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); + sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); + sDefaults.putString(KEY_CALL_COMPOSER_PICTURE_SERVER_URL_STRING, ""); sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 9b51e4a52b9e..886ec33af2b8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -8105,6 +8105,7 @@ public class TelephonyManager { * * @hide */ + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode() { try { @@ -9463,7 +9464,7 @@ public class TelephonyManager { } /** @hide */ - @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = { + @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = { CDMA_SUBSCRIPTION_UNKNOWN, CDMA_SUBSCRIPTION_RUIM_SIM, CDMA_SUBSCRIPTION_NV diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java index beec4a680d78..0d82a54a0f32 100644 --- a/telephony/java/android/telephony/ims/DelegateMessageCallback.java +++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; /** @@ -30,6 +31,7 @@ import android.telephony.ims.stub.SipDelegate; * </ul> * @hide */ +@SystemApi public interface DelegateMessageCallback { /** diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java index 4facfa77de21..3558a9b79ce0 100644 --- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java +++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java @@ -18,14 +18,14 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.util.ArraySet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.Set; @@ -34,6 +34,7 @@ import java.util.Set; * ImsService. * @hide */ +@SystemApi public final class DelegateRegistrationState implements Parcelable { /** @@ -114,14 +115,14 @@ public final class DelegateRegistrationState implements Parcelable { }) public @interface DeregisteringReason {} - private final ArrayList<String> mRegisteredTags = new ArrayList<>(); - private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>(); - private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>(); + private ArraySet<String> mRegisteredTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>(); + private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>(); /** * Builder used to create new instances of {@link DelegateRegistrationState}. */ - public static class Builder { + public static final class Builder { private final DelegateRegistrationState mState; @@ -135,10 +136,8 @@ public final class DelegateRegistrationState implements Parcelable { * @param featureTag The IMS media feature tag included in the current IMS registration. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addRegisteredFeatureTag(@NonNull String featureTag) { - if (!mState.mRegisteredTags.contains(featureTag)) { - mState.mRegisteredTags.add(featureTag); - } + public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) { + mState.mRegisteredTags.add(featureTag); return this; } @@ -148,7 +147,8 @@ public final class DelegateRegistrationState implements Parcelable { * @param featureTags The IMS media feature tags included in the current IMS registration. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) { mState.mRegisteredTags.addAll(featureTags); return this; } @@ -167,13 +167,9 @@ public final class DelegateRegistrationState implements Parcelable { * The availability of the feature tag depends on the {@link DeregisteringReason}. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addDeregisteringFeatureTag(@NonNull String featureTag, + public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag, @DeregisteringReason int reason) { - boolean ftExists = mState.mDeregisteringTags.stream().anyMatch( - f -> f.getFeatureTag().equals(featureTag)); - if (!ftExists) { - mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); - } + mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason)); return this; } @@ -185,20 +181,16 @@ public final class DelegateRegistrationState implements Parcelable { * @param reason The reason why the media feature tag has been deregistered. * @return The in-progress Builder instance for RegistrationState. */ - public Builder addDeregisteredFeatureTag(@NonNull String featureTag, + public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag, @DeregisteredReason int reason) { - boolean ftExists = mState.mDeregisteredTags.stream().anyMatch( - f -> f.getFeatureTag().equals(featureTag)); - if (!ftExists) { - mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); - } + mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason)); return this; } /** * @return the finalized instance. */ - public DelegateRegistrationState build() { + public @NonNull DelegateRegistrationState build() { return mState; } } @@ -212,7 +204,7 @@ public final class DelegateRegistrationState implements Parcelable { * Used for unparcelling only. */ private DelegateRegistrationState(Parcel source) { - source.readList(mRegisteredTags, null /*classloader*/); + mRegisteredTags = (ArraySet<String>) source.readArraySet(null); readStateFromParcel(source, mDeregisteringTags); readStateFromParcel(source, mDeregisteredTags); } @@ -268,7 +260,8 @@ public final class DelegateRegistrationState implements Parcelable { return new ArraySet<>(mDeregisteredTags); } - public static final Creator<DelegateRegistrationState> CREATOR = + + public static final @NonNull Creator<DelegateRegistrationState> CREATOR = new Creator<DelegateRegistrationState>() { @Override public DelegateRegistrationState createFromParcel(Parcel source) { @@ -287,13 +280,13 @@ public final class DelegateRegistrationState implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeList(mRegisteredTags); + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeArraySet(mRegisteredTags); writeStateToParcel(dest, mDeregisteringTags); writeStateToParcel(dest, mDeregisteredTags); } - private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) { + private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) { dest.writeInt(state.size()); for (FeatureTagState s : state) { dest.writeString(s.getFeatureTag()); @@ -301,11 +294,12 @@ public final class DelegateRegistrationState implements Parcelable { } } - private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) { + private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) { int len = source.readInt(); for (int i = 0; i < len; i++) { String ft = source.readString(); int reason = source.readInt(); + emptyState.add(new FeatureTagState(ft, reason)); } } diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java index 73d0840177dd..c322d924182a 100644 --- a/telephony/java/android/telephony/ims/DelegateRequest.java +++ b/telephony/java/android/telephony/ims/DelegateRequest.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.stub.SipDelegate; @@ -31,6 +32,7 @@ import java.util.Set; * SipDelegateConnection given back to the requesting application. * @hide */ +@SystemApi public final class DelegateRequest implements Parcelable { private final ArrayList<String> mFeatureTags; @@ -52,7 +54,7 @@ public final class DelegateRequest implements Parcelable { * @return the list of IMS feature tag associated with this DelegateRequest in the format * defined in RCC.07 section 2.6.1.3. */ - public Set<String> getFeatureTags() { + public @NonNull Set<String> getFeatureTags() { return new ArraySet<>(mFeatureTags); } @@ -70,7 +72,7 @@ public final class DelegateRequest implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeList(mFeatureTags); } diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java index 0f1afc42249e..fb659490d546 100644 --- a/telephony/java/android/telephony/ims/DelegateStateCallback.java +++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java @@ -18,10 +18,11 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; import android.telephony.ims.stub.SipTransportImplBase; -import java.util.List; +import java.util.Set; /** * Callback interface to notify a remote application of the following: @@ -34,26 +35,24 @@ import java.util.List; * </ul> * @hide */ +@SystemApi public interface DelegateStateCallback { /** * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is * called by the framework to notify the framework and remote application that the * {@link SipDelegate} has been successfully created. - * - * @param delegate The SipDelegate created to service the DelegateRequest. - * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags + * @param delegate The SipDelegate created to service the DelegateRequest. + * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags * associated with this {@link SipDelegate} that have no access to send/receive SIP messages * as well as a reason for why the feature tag is denied. For more information on the reason * why the feature tag was denied access, see the * {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due * to this {@link SipDelegate} not supporting a feature or this ImsService already * implementing this feature elsewhere. If all features of this {@link SipDelegate} are - * denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will - * later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the - * delegate up. + * denied, this method should still be called. */ - void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags); + void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags); /** * This must be called by the ImsService after the framework calls diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java index 060be6f2510d..3622065c5fe8 100644 --- a/telephony/java/android/telephony/ims/FeatureTagState.java +++ b/telephony/java/android/telephony/ims/FeatureTagState.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.stub.DelegateConnectionStateCallback; @@ -39,6 +40,7 @@ import java.util.Objects; * currently available. * @hide */ +@SystemApi public final class FeatureTagState implements Parcelable { private final String mFeatureTag; @@ -48,8 +50,8 @@ public final class FeatureTagState implements Parcelable { * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState} * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged( * DelegateRegistrationState, List)} and - * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this - * is used. + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and + * when this is used. * * @param featureTag The IMS feature tag that is deregistered, in the process of * deregistering, or denied. @@ -93,12 +95,12 @@ public final class FeatureTagState implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mFeatureTag); dest.writeInt(mState); } - public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { + public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() { @Override public FeatureTagState createFromParcel(Parcel source) { return new FeatureTagState(source); diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 1b51936e873b..aaa68d6f7d57 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -18,6 +18,7 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -207,6 +208,42 @@ public final class ImsCallProfile implements Parcelable { "android.telephony.ims.extra.RETRY_CALL_FAIL_NETWORKTYPE"; /** + * Extra for the call composer call priority, either {@link ImsCallProfile#PRIORITY_NORMAL} or + * {@link ImsCallProfile#PRIORITY_URGENT}. It can be set via + * {@link #setCallExtraInt(String, int)}. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final String EXTRA_PRIORITY = "android.telephony.ims.extra.PRIORITY"; + + // TODO(hallliu) remove the reference to the maximum length and update it later. + /** + * Extra for the call composer call subject, a string of maximum length 60 characters. + * It can be set via {@link #setCallExtra(String, String)}. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_CALL_SUBJECT = "android.telephony.ims.extra.CALL_SUBJECT"; + + /** + * Extra for the call composer call location, an {@Link android.location.Location} parcelable + * class to represent the geolocation as a latitude and longitude pair. It can be set via + * {@link #setCallExtraParcelable(String, Parcelable)}. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_LOCATION = "android.telephony.ims.extra.LOCATION"; + + /** + * Extra for the call composer picture URL, a String that indicates the URL on the carrier’s + * server infrastructure to get the picture. It can be set via + * {@link #setCallExtra(String, String)}. + * + * Reference: RCC.20 Section 2.4.3.2 + */ + public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL"; + + /** * Values for EXTRA_OIR / EXTRA_CNAP */ /** @@ -244,6 +281,21 @@ public final class ImsCallProfile implements Parcelable { */ public static final int DIALSTRING_USSD = 2; + // Values for EXTRA_PRIORITY + /** + * Indicates the call composer call priority is normal. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_NORMAL = 0; + + /** + * Indicates the call composer call priority is urgent. + * + * Reference: RCC.20 Section 2.4.4.2 + */ + public static final int PRIORITY_URGENT = 1; + /** * Call is not restricted on peer side and High Definition media is supported */ @@ -588,6 +640,19 @@ public final class ImsCallProfile implements Parcelable { return mCallExtras.getInt(name, defaultValue); } + /** + * Get the call extras (Parcelable), given the extra name. + * @param name call extra name + * @return the corresponding call extra Parcelable or null if not applicable + */ + @Nullable + public <T extends Parcelable> T getCallExtraParcelable(@Nullable String name) { + if (mCallExtras != null) { + return mCallExtras.getParcelable(name); + } + return null; + } + public void setCallExtra(String name, String value) { if (mCallExtras != null) { mCallExtras.putString(name, value); @@ -607,6 +672,17 @@ public final class ImsCallProfile implements Parcelable { } /** + * Set the call extra value (Parcelable), given the call extra name. + * @param name call extra name + * @param parcelable call extra value + */ + public void setCallExtraParcelable(@NonNull String name, @NonNull Parcelable parcelable) { + if (mCallExtras != null) { + mCallExtras.putParcelable(name, parcelable); + } + } + + /** * Set the call restrict cause, which provides the reason why a call has been restricted from * using High Definition media. */ diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index fdf636c323b6..c663e393fe06 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -49,8 +49,7 @@ public final class ImsExternalCallState implements Parcelable { public static final int CALL_STATE_TERMINATED = 2; /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { CALL_STATE_CONFIRMED, CALL_STATE_TERMINATED }, @@ -59,8 +58,7 @@ public final class ImsExternalCallState implements Parcelable { public @interface ExternalCallState {} /**@hide*/ - @IntDef(flag = true, - value = { + @IntDef(value = { ImsCallProfile.CALL_TYPE_VOICE, ImsCallProfile.CALL_TYPE_VT_TX, ImsCallProfile.CALL_TYPE_VT_RX, diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index fb8e5d37875b..868dea6a3121 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -72,7 +72,7 @@ public final class ImsSsData implements Parcelable { /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ACTIVATION, SS_DEACTIVATION, SS_INTERROGATION, @@ -89,7 +89,7 @@ public final class ImsSsData implements Parcelable { public static final int SS_ERASURE = 4; /**@hide*/ - @IntDef(flag = true, prefix = {"SS_"}, value = { + @IntDef(prefix = {"SS_"}, value = { SS_ALL_TELE_AND_BEARER_SERVICES, SS_ALL_TELESEVICES, SS_TELEPHONY, @@ -190,7 +190,7 @@ public final class ImsSsData implements Parcelable { public static final int RESULT_SUCCESS = 0; /** @hide */ - @IntDef(flag = true, prefix = { "SS_" }, value = { + @IntDef(prefix = { "SS_" }, value = { SS_CFU, SS_CF_BUSY, SS_CF_NO_REPLY, diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index d12a6aef5186..5848be8b0bf2 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -105,10 +105,17 @@ public final class RcsContactUceCapability implements Parcelable { public @interface RequestResult {} /** + * The base class of {@link OptionsBuilder} and {@link PresenceBuilder} + */ + public static abstract class RcsUcsCapabilityBuilder { + public abstract @NonNull RcsContactUceCapability build(); + } + + /** * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through SIP OPTIONS. */ - public static class OptionsBuilder { + public static class OptionsBuilder extends RcsUcsCapabilityBuilder { private final RcsContactUceCapability mCapabilities; @@ -155,6 +162,7 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ + @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -164,7 +172,7 @@ public final class RcsContactUceCapability implements Parcelable { * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were * queried through a presence server. */ - public static class PresenceBuilder { + public static class PresenceBuilder extends RcsUcsCapabilityBuilder { private final RcsContactUceCapability mCapabilities; @@ -205,6 +213,7 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ + @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java index 6bfdc2c6d48a..c3cc1edf590b 100644 --- a/telephony/java/android/telephony/ims/SipDelegateConnection.java +++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java @@ -17,6 +17,7 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.stub.SipDelegate; /** @@ -36,6 +37,7 @@ import android.telephony.ims.stub.SipDelegate; * @see SipDelegateManager#createSipDelegate * @hide */ +@SystemApi public interface SipDelegateConnection { /** @@ -47,9 +49,8 @@ public interface SipDelegateConnection { * @param sipMessage The SipMessage to be sent. * @param configVersion The SipDelegateImsConfiguration version used to construct the * SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more - * information on this parameter and why it is used. */ - void sendMessage(@NonNull SipMessage sipMessage, int configVersion); + void sendMessage(@NonNull SipMessage sipMessage, long configVersion); /** * Notify the {@link SipDelegate} that a SIP message received from diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java index 8abd0ee94865..eddbb1002f20 100644 --- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java +++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java @@ -17,7 +17,10 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StringDef; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.PersistableBundle; @@ -47,7 +50,8 @@ import java.lang.annotation.RetentionPolicy; * update. * @hide */ -public class SipDelegateImsConfiguration implements Parcelable { +@SystemApi +public final class SipDelegateImsConfiguration implements Parcelable { /** * IPV4 Address type. @@ -354,7 +358,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Builder class to be used when constructing a new SipDelegateImsConfiguration. */ - public static class Builder { + public static final class Builder { private final long mVersion; private final PersistableBundle mBundle; @@ -381,7 +385,10 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Put a string value into this configuration bundle for the given key. */ - public Builder putString(@StringConfigKey String key, String value) { + // getString is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addString(@NonNull @StringConfigKey String key, + @NonNull String value) { mBundle.putString(key, value); return this; } @@ -389,7 +396,9 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Replace the existing default value with a new value for a given key. */ - public Builder putInt(@IntConfigKey String key, int value) { + // getInt is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) { mBundle.putInt(key, value); return this; } @@ -397,7 +406,9 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * Replace the existing default value with a new value for a given key. */ - public Builder putBoolean(@BooleanConfigKey String key, boolean value) { + // getBoolean is available below. + @SuppressLint("MissingGetterMatchingBuilder") + public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) { mBundle.putBoolean(key, value); return this; } @@ -405,7 +416,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * @return a new SipDelegateImsConfiguration from this Builder. */ - public SipDelegateImsConfiguration build() { + public @NonNull SipDelegateImsConfiguration build() { return new SipDelegateImsConfiguration(mVersion, mBundle); } } @@ -424,30 +435,38 @@ public class SipDelegateImsConfiguration implements Parcelable { } /** + * @return {@code true} if this configuration object has a an entry for the key specified, + * {@code false} if it does not. + */ + public boolean containsKey(@NonNull String key) { + return mBundle.containsKey(key); + } + + /** * @return the string value associated with a given key or {@code null} if it doesn't exist. */ - public @StringConfigKey String getString(String key) { + public @Nullable @StringConfigKey String getString(@NonNull String key) { return mBundle.getString(key); } /** - * @return the Integer value associated with a given key or {@code null} if the value doesn't - * exist. + * @return the integer value associated with a given key if it exists or the supplied default + * value if it does not. */ - public @IntConfigKey Integer getInt(String key) { + public @IntConfigKey int getInt(@NonNull String key, int defaultValue) { if (!mBundle.containsKey(key)) { - return null; + return defaultValue; } return mBundle.getInt(key); } /** - * @return the Integer value associated with a given key or {@code null} if the value doesn't - * exist. + * @return the boolean value associated with a given key or the supplied default value if the + * value doesn't exist in the bundle. */ - public @BooleanConfigKey Boolean getBoolen(String key) { + public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) { if (!mBundle.containsKey(key)) { - return null; + return defaultValue; } return mBundle.getBoolean(key); } @@ -455,7 +474,7 @@ public class SipDelegateImsConfiguration implements Parcelable { /** * @return a shallow copy of the full configuration. */ - public PersistableBundle copyBundle() { + public @NonNull PersistableBundle copyBundle() { return new PersistableBundle(mBundle); } @@ -479,12 +498,12 @@ public class SipDelegateImsConfiguration implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mVersion); dest.writePersistableBundle(mBundle); } - public static final Creator<SipDelegateImsConfiguration> CREATOR = + public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR = new Creator<SipDelegateImsConfiguration>() { @Override public SipDelegateImsConfiguration createFromParcel(Parcel source) { diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java index 190a792b5a9a..2ec88ff27f93 100644 --- a/telephony/java/android/telephony/ims/SipDelegateManager.java +++ b/telephony/java/android/telephony/ims/SipDelegateManager.java @@ -54,7 +54,6 @@ public class SipDelegateManager { * The SIP message has failed being sent or received for an unknown reason. * <p> * The caller should retry a message that failed with this response. - * @hide */ public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; @@ -64,47 +63,40 @@ public class SipDelegateManager { * <p> * This is considered a permanent error and the system will automatically begin the teardown and * destruction of the SipDelegate. No further messages should be sent on this transport. - * @hide */ public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; /** * The message has not been sent/received because the delegate is in the process of closing and * has become unavailable. No further messages should be sent/received on this delegate. - * @hide */ public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; /** * The SIP message has an invalid start line and the message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; /** * One or more of the header fields in the header section of the outgoing SIP message is invalid * and the SIP message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; /** * The body content of the SIP message is invalid and the message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; /** * The feature tag associated with the outgoing message does not match any known feature tags * and this message can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; /** * The feature tag associated with the outgoing message is not enabled for the associated * SipDelegateConnection and can not be sent. - * @hide */ public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; @@ -113,7 +105,6 @@ public class SipDelegateManager { * <p> * This message should be retried when connectivity to the network is re-established. See * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined. - * @hide */ public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; @@ -124,7 +115,6 @@ public class SipDelegateManager { * This is considered a temporary failure, the message should not be retried until an IMS * registration change callback is received via * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged} - * @hide */ public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; @@ -135,7 +125,6 @@ public class SipDelegateManager { * <p> * The @link SipMessage} should be recreated using the newest * {@link SipDelegateImsConfiguration} and sent again. - * @hide */ public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; @@ -146,7 +135,6 @@ public class SipDelegateManager { * This is considered a temporary error and the {@link SipDelegateConnection} should resend the * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is * no longer reported. - * @hide */ public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; @@ -171,7 +159,6 @@ public class SipDelegateManager { /** * Access to use this feature tag has been denied for an unknown reason. - * @hide */ public static final int DENIED_REASON_UNKNOWN = 0; @@ -179,14 +166,12 @@ public class SipDelegateManager { * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by * another SipDelegateConnection and can not be associated with this delegate. The feature tag * will stay in this state until the feature tag is release by the other application. - * @hide */ public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; /** * Access to use this feature tag has been denied because this application does not have the * permissions required to access this feature tag. - * @hide */ public static final int DENIED_REASON_NOT_ALLOWED = 2; @@ -194,14 +179,12 @@ public class SipDelegateManager { * Access to use this feature tag has been denied because single registration is not allowed by * the carrier at this time. The application should fall back to dual registration if * applicable. - * @hide */ public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; /** * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been * denied. - * @hide */ public static final int DENIED_REASON_INVALID = 4; @@ -218,33 +201,28 @@ public class SipDelegateManager { /** * The SipDelegate has closed due to an unknown reason. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; /** * The SipDelegate has closed because the IMS service has died unexpectedly. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; /** * The SipDelegate has closed because the IMS application has requested that the connection be * destroyed. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; /** * The SipDelegate has been closed due to the user disabling RCS. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; /** * The SipDelegate has been closed due to the subscription associated with this delegate being * torn down. - * @hide */ public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; @@ -331,7 +309,6 @@ public class SipDelegateManager { * SipDelegateConnection. * @throws ImsException Thrown if there was a problem communicating with the ImsService * associated with this SipDelegateManager. See {@link ImsException#getCode()}. - * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor, @@ -366,7 +343,6 @@ public class SipDelegateManager { * This will also clean up all related callbacks in the associated ImsService. * @param delegateConnection The SipDelegateConnection to destroy. * @param reason The reason for why this SipDelegateConnection was destroyed. - * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection, diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java index c3b1be2d7fc8..1539224dedcf 100644 --- a/telephony/java/android/telephony/ims/SipMessage.java +++ b/telephony/java/android/telephony/ims/SipMessage.java @@ -17,10 +17,14 @@ package android.telephony.ims; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; +import java.util.Objects; + /** * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP * messages are structured and used. @@ -29,6 +33,7 @@ import android.os.Parcelable; * verification and should not be used as a generic SIP message container. * @hide */ +@SystemApi public final class SipMessage implements Parcelable { // Should not be set to true for production! private static final boolean IS_DEBUGGING = Build.IS_ENG; @@ -95,14 +100,14 @@ public final class SipMessage implements Parcelable { } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mStartLine); dest.writeString(mHeaderSection); dest.writeInt(mContent.length); dest.writeByteArray(mContent); } - public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() { + public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() { @Override public SipMessage createFromParcel(Parcel source) { return new SipMessage(source); @@ -152,4 +157,21 @@ public final class SipMessage implements Parcelable { } return startLine; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SipMessage that = (SipMessage) o; + return mStartLine.equals(that.mStartLine) + && mHeaderSection.equals(that.mHeaderSection) + && Arrays.equals(mContent, that.mContent); + } + + @Override + public int hashCode() { + int result = Objects.hash(mStartLine, mHeaderSection); + result = 31 * result + Arrays.hashCode(mContent); + return result; + } } diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl index 477ee958e1e8..5d6766a65155 100644 --- a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl @@ -23,7 +23,7 @@ import android.telephony.ims.SipMessage; * {@hide} */ oneway interface ISipDelegate { - void sendMessage(in SipMessage sipMessage, int configVersion); + void sendMessage(in SipMessage sipMessage, long configVersion); void notifyMessageReceived(in String viaTransactionId); void notifyMessageReceiveError(in String viaTransactionId, int reason); diff --git a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl index 4deaba1b7a49..a14199365b07 100644 --- a/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/ISubscribeResponseCallback.aidl @@ -32,5 +32,5 @@ oneway interface ISubscribeResponseCallback { void onNetworkResponse(int code, in String reason); void onNotifyCapabilitiesUpdate(in List<String> pidfXmls); void onResourceTerminated(in List<RcsContactTerminatedReason> uriTerminatedReason); - void onTerminated(in String reason, in String retryAfter); + void onTerminated(in String reason, long retryAfterMilliseconds); } diff --git a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java index 37588ed98585..1fb339c0cf89 100644 --- a/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/RcsSubscribeResponseAidlWrapper.java @@ -86,9 +86,9 @@ public class RcsSubscribeResponseAidlWrapper implements SubscribeResponseCallbac } @Override - public void onTerminated(String reason, String retryAfter) throws ImsException { + public void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException { try { - mResponseBinder.onTerminated(reason, retryAfter); + mResponseBinder.onTerminated(reason, retryAfterMilliseconds); } catch (RemoteException e) { } } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java index a7f62cc32be1..522ad8160870 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java @@ -29,12 +29,13 @@ import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; import android.telephony.ims.stub.SipDelegate; -import java.util.List; +import java.util.ArrayList; +import java.util.Set; import java.util.concurrent.Executor; /** * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements - * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called + * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called * in order to trampoline events back to telephony. * @hide */ @@ -42,7 +43,7 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() { @Override - public void sendMessage(SipMessage sipMessage, int configVersion) { + public void sendMessage(SipMessage sipMessage, long configVersion) { SipDelegate d = mDelegate; final long token = Binder.clearCallingIdentity(); try { @@ -136,10 +137,10 @@ public class SipDelegateAidlWrapper implements DelegateStateCallback, DelegateMe @Override public void onCreated(@NonNull SipDelegate delegate, - @Nullable List<FeatureTagState> deniedTags) { + @Nullable Set<FeatureTagState> deniedTags) { mDelegate = delegate; try { - mStateBinder.onCreated(mDelegateBinder, deniedTags); + mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags)); } catch (RemoteException e) { // BinderDied will trigger destroySipDelegate, so just ignore this locally. } diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java index 3bd1a462b31a..29ba8e2d50c4 100644 --- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java +++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java @@ -158,7 +158,7 @@ public class SipDelegateConnectionAidlWrapper implements SipDelegateConnection, } @Override - public void sendMessage(SipMessage sipMessage, int configVersion) { + public void sendMessage(SipMessage sipMessage, long configVersion) { try { ISipDelegate conn = getSipDelegateBinder(); if (conn == null) { diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java index 59f9601299b2..eefe8493aef1 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.SipDelegateConnection; import android.telephony.ims.SipDelegateManager; import android.telephony.ims.SipMessage; @@ -26,6 +27,7 @@ import android.telephony.ims.SipMessage; * messages as well as the result of sending a SIP message. * @hide */ +@SystemApi public interface DelegateConnectionMessageCallback { /** @@ -49,6 +51,6 @@ public interface DelegateConnectionMessageCallback { * previously sent {@link SipMessage}. * @param reason The reason for the failure. */ - void onMessageSendFailure(String viaTransactionId, + void onMessageSendFailure(@NonNull String viaTransactionId, @SipDelegateManager.MessageFailureReason int reason); } diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java index 976180538b18..02218ead0ad7 100644 --- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java +++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.DelegateRegistrationState; import android.telephony.ims.DelegateRequest; import android.telephony.ims.FeatureTagState; @@ -58,6 +59,7 @@ import java.util.Set; * * @hide */ +@SystemApi public interface DelegateConnectionStateCallback { /** diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index a6f5c45445f5..153d687dd84f 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -49,8 +49,7 @@ public class ImsRegistrationImplBase { * @hide */ // Defines the underlying radio technology type that we have registered for IMS over. - @IntDef(flag = true, - value = { + @IntDef(value = { REGISTRATION_TECH_NONE, REGISTRATION_TECH_LTE, REGISTRATION_TECH_IWLAN diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index b5704bfb3569..3a0fb6edb2fb 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -250,7 +250,7 @@ public class RcsCapabilityExchangeImplBase { * This allows the framework to know that there will no longer be any * capability updates for the requested operationToken. */ - void onTerminated(String reason, String retryAfter) throws ImsException; + void onTerminated(String reason, long retryAfterMilliseconds) throws ImsException; } diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java index 3ec97095eb00..d7e7b62dd550 100644 --- a/telephony/java/android/telephony/ims/stub/SipDelegate.java +++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.telephony.ims.DelegateMessageCallback; import android.telephony.ims.ImsService; import android.telephony.ims.SipDelegateImsConfiguration; @@ -40,6 +41,7 @@ import android.telephony.ims.SipMessage; * {@link android.telephony.ims.DelegateStateCallback} for more information. * @hide */ +@SystemApi public interface SipDelegate { /** @@ -57,7 +59,7 @@ public interface SipDelegate { * {@link DelegateMessageCallback#onMessageSendFailure} should be called with code * {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}. */ - void sendMessage(@NonNull SipMessage message, int configVersion); + void sendMessage(@NonNull SipMessage message, long configVersion); /** * The framework is requesting that routing resources associated with the SIP dialog using the diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java index 93d438cf7f4d..1f74c09af0f6 100644 --- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -17,6 +17,7 @@ package android.telephony.ims.stub; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Binder; import android.os.IBinder; @@ -32,7 +33,6 @@ import android.telephony.ims.aidl.SipDelegateAidlWrapper; import android.util.Log; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -99,7 +99,8 @@ public class SipTransportImplBase { /** * Called by the Telephony framework to request the creation of a new {@link SipDelegate}. * <p> - * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with + * The implementation must call + * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with * the {@link SipDelegate} that is associated with the {@link DelegateRequest}. * <p> * This method will be called on the Executor specified in @@ -112,8 +113,9 @@ public class SipTransportImplBase { * for the SipDelegate. * @param mc A callback back to the remote application to be used to send SIP messages to the * remote application and acknowledge the sending of outgoing SIP messages. - * @hide */ + // executor used is defined in the constructor. + @SuppressLint("ExecutorRegistration") public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request, @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) { throw new UnsupportedOperationException("createSipDelegate not implemented!"); @@ -130,7 +132,6 @@ public class SipTransportImplBase { * @param delegate The delegate to be destroyed. * @param reason The reason the remote connection to this {@link SipDelegate} is being * destroyed. - * @hide */ public void destroySipDelegate(@NonNull SipDelegate delegate, @SipDelegateManager.SipDelegateDestroyReason int reason) { diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index b524549440da..5d4fdd0ff556 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -857,6 +857,11 @@ interface ITelephony { in int[] featureTypes, in String packageName); /** + * @return true if the ImsService cleared any carrier ImsService overrides, false otherwise. + */ + boolean clearCarrierImsServiceOverride(int slotIndex); + + /** * @return the package name of the carrier/device ImsService associated with this slot. */ String getBoundImsServicePackage(int slotIndex, boolean isCarrierImsService, int featureType); diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING new file mode 100644 index 000000000000..1b569f9455bf --- /dev/null +++ b/tests/BootImageProfileTest/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "BootImageProfileTest" + } + ] +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index a20f96d17278..67deca4fe387 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.close +import androidx.test.filters.FlakyTest import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -54,6 +55,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 174635878) class CloseAppBackButtonTest( testName: String, flickerSpec: Flicker diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 4bbb38c4d71a..252ce2a32bf0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,6 +16,7 @@ package com.android.server.wm.flicker.close +import androidx.test.filters.FlakyTest import android.view.Surface import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry @@ -54,6 +55,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 174635878) class CloseAppHomeButtonTest( testName: String, flickerSpec: Flicker diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 1f03c4dc056d..686ddcbd66bd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -105,8 +105,7 @@ class OpenAppColdTest( configuration.endRotation) navBarLayerIsAlwaysVisible(enabled = false) statusBarLayerIsAlwaysVisible(enabled = false) - visibleLayersShownMoreThanOneConsecutiveEntry( - enabled = Surface.ROTATION_0 == configuration.endRotation) + visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 174541970) appLayerReplacesWallpaperLayer(testApp) } diff --git a/tests/RollbackTest/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml index 9274da268735..590105b3209f 100644 --- a/tests/RollbackTest/RollbackTest/AndroidManifest.xml +++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml @@ -19,8 +19,6 @@ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <application> - <receiver android:name="com.android.cts.install.lib.LocalIntentSender" - android:exported="true" /> <uses-library android:name="android.test.runner" /> </application> diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 52718bec9148..3d8deb5cfc8d 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -34,7 +34,6 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; -import com.android.cts.install.lib.LocalIntentSender; import com.android.cts.install.lib.TestApp; import com.android.cts.install.lib.Uninstall; import com.android.cts.rollback.lib.Rollback; @@ -258,10 +257,6 @@ public class StagedRollbackTest { .getPackageManager().getPackageInstaller(); pi.abandonSession(sessionId); - // Remove the first intent sender result, so that the next staged install session does not - // erroneously think that it has itself been abandoned. - // TODO(b/136260017): Restructure LocalIntentSender to negate the need for this step. - LocalIntentSender.getIntentSenderResult(); Install.single(TestApp.A2).setStaged().setEnableRollback().commit(); } diff --git a/tests/StagedInstallTest/app/AndroidManifest.xml b/tests/StagedInstallTest/app/AndroidManifest.xml index a678f1ec3691..d7ac9d0f9ce4 100644 --- a/tests/StagedInstallTest/app/AndroidManifest.xml +++ b/tests/StagedInstallTest/app/AndroidManifest.xml @@ -20,8 +20,6 @@ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <application> - <receiver android:name="com.android.cts.install.lib.LocalIntentSender" - android:exported="true" /> <uses-library android:name="android.test.runner" /> </application> diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java index b77ed6ab5a29..cade5ba3771f 100644 --- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java +++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java @@ -22,10 +22,13 @@ import static com.android.testutils.ParcelUtils.assertParcelSane; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.os.Build; import android.util.SparseArray; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; +import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -34,7 +37,8 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; -@RunWith(AndroidJUnit4.class) +@IgnoreUpTo(Build.VERSION_CODES.R) +@RunWith(DevSdkIgnoreRunner.class) @SmallTest public class OemNetworkPreferencesTest { diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 3619937d76ed..5d4573716145 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -56,8 +56,10 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IA; import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; @@ -1058,7 +1060,9 @@ public class ConnectivityServiceTest { public void setUids(Set<UidRange> uids) { mNetworkCapabilities.setUids(uids); - updateCapabilitiesInternal(null /* defaultNetwork */, true); + if (mAgentRegistered) { + mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true); + } } public void setVpnType(int vpnType) { @@ -1089,6 +1093,10 @@ public class ConnectivityServiceTest { mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp, mNetworkCapabilities); mMockNetworkAgent.waitForIdle(TIMEOUT_MS); + verify(mNetworkManagementService, times(1)) + .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0]))); + verify(mNetworkManagementService, never()) + .removeVpnUidRanges(eq(mMockVpn.getNetId()), any()); mAgentRegistered = true; mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities()); mNetworkAgent = mMockNetworkAgent.getNetworkAgent(); @@ -1143,28 +1151,6 @@ public class ConnectivityServiceTest { mMockNetworkAgent.sendLinkProperties(lp); } - private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork, - boolean sendToConnectivityService) { - if (!mAgentRegistered) return null; - super.updateCapabilities(defaultNetwork); - // Because super.updateCapabilities will update the capabilities of the agent but - // not the mock agent, the mock agent needs to know about them. - copyCapabilitiesToNetworkAgent(sendToConnectivityService); - return new NetworkCapabilities(mNetworkCapabilities); - } - - private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) { - if (null != mMockNetworkAgent) { - mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, - sendToConnectivityService); - } - } - - @Override - public NetworkCapabilities updateCapabilities(Network defaultNetwork) { - return updateCapabilitiesInternal(defaultNetwork, false); - } - public void disconnect() { if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect(); mAgentRegistered = false; @@ -5414,6 +5400,106 @@ public class ConnectivityServiceTest { } @Test + public void testApplyUnderlyingCapabilities() throws Exception { + mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR); + mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); + mCellNetworkAgent.connect(false /* validated */); + mWiFiNetworkAgent.connect(false /* validated */); + + final NetworkCapabilities cellNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .setLinkDownstreamBandwidthKbps(10); + final NetworkCapabilities wifiNc = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_NOT_ROAMING) + .addCapability(NET_CAPABILITY_NOT_CONGESTED) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .setLinkUpstreamBandwidthKbps(20); + mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */); + mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */); + waitForIdle(); + + final Network mobile = mCellNetworkAgent.getNetwork(); + final Network wifi = mWiFiNetworkAgent.getNetwork(); + + final NetworkCapabilities initialCaps = new NetworkCapabilities(); + initialCaps.addCapability(NET_CAPABILITY_INTERNET); + initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withNoUnderlying = new NetworkCapabilities(); + withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING); + withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED); + withNoUnderlying.addTransportType(TRANSPORT_VPN); + withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN); + + final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying); + withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED); + withMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + + final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying); + withWifiUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + withWifiUnderlying.setLinkUpstreamBandwidthKbps(20); + + final NetworkCapabilities withWifiAndMobileUnderlying = + new NetworkCapabilities(withNoUnderlying); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR); + withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING); + withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10); + withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20); + + NetworkCapabilities caps = new NetworkCapabilities(initialCaps); + final boolean notDeclaredMetered = false; + mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered); + assertEquals(withNoUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered); + assertEquals(withMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + + final boolean isDeclaredMetered = true; + withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED); + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + caps, notDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + caps = new NetworkCapabilities(initialCaps); + mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi}, + caps, notDeclaredMetered); + assertEquals(withWifiAndMobileUnderlying, caps); + + mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered); + assertEquals(withWifiUnderlying, caps); + } + + @Test public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception { final TestNetworkCallback callback = new TestNetworkCallback(); final NetworkRequest request = new NetworkRequest.Builder() @@ -5963,17 +6049,28 @@ public class ConnectivityServiceTest { && caps.hasTransport(TRANSPORT_VPN) && caps.hasTransport(TRANSPORT_WIFI)); + // Change the VPN's capabilities somehow (specifically, disconnect wifi). + mWiFiNetworkAgent.disconnect(); + callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent); + callback.expectCapabilitiesThat(mMockVpn, (caps) + -> caps.getUids().size() == 2 + && caps.getUids().contains(new UidRange(uid, uid)) + && caps.getUids().contains(UidRange.createForUser(restrictedUserId)) + && caps.hasTransport(TRANSPORT_VPN) + && !caps.hasTransport(TRANSPORT_WIFI)); + // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user. final Intent removedIntent = new Intent(ACTION_USER_REMOVED); removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId); handler.post(() -> mServiceContext.sendBroadcast(removedIntent)); - // Expect that the VPN gains the UID range for the restricted user. + // Expect that the VPN gains the UID range for the restricted user, and that the capability + // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved. callback.expectCapabilitiesThat(mMockVpn, (caps) -> caps.getUids().size() == 1 && caps.getUids().contains(new UidRange(uid, uid)) && caps.hasTransport(TRANSPORT_VPN) - && caps.hasTransport(TRANSPORT_WIFI)); + && !caps.hasTransport(TRANSPORT_WIFI)); } @Test @@ -6922,8 +7019,8 @@ public class ConnectivityServiceTest { final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER)); mMockVpn.establish(lp, VPN_UID, vpnRange); - // Connected VPN should have interface rules set up. There are two expected invocations, - // one during VPN uid update, one during VPN LinkProperties update + // A connected VPN should have interface rules set up. There are two expected invocations, + // one during the VPN initial connection, one during the VPN LinkProperties update. ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class); verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture()); assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID); @@ -7438,20 +7535,14 @@ public class ConnectivityServiceTest { setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); - // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be - // active - final VpnInfo info = new VpnInfo(); - info.ownerUid = Process.myUid(); - info.vpnIface = VPN_IFNAME; - mMockVpn.setVpnInfo(info); - mMockVpn.establishForMyUid(); - waitForIdle(); + // Wait for networks to connect and broadcasts to be sent before removing permissions. + waitForIdle(); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); - assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network})); + waitForIdle(); assertTrue( "Active VPN permission not applied", mService.checkConnectivityDiagnosticsPermissions( @@ -7459,6 +7550,7 @@ public class ConnectivityServiceTest { mContext.getOpPackageName())); assertTrue(mService.setUnderlyingNetworksForVpn(null)); + waitForIdle(); assertFalse( "VPN shouldn't receive callback on non-underlying network", mService.checkConnectivityDiagnosticsPermissions( diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 1dcc07c6db81..337507ac1d46 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -21,15 +21,6 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.content.pm.UserInfo.FLAG_PRIMARY; import static android.content.pm.UserInfo.FLAG_RESTRICTED; import static android.net.ConnectivityManager.NetworkCallback; -import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_VPN; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -41,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; @@ -86,10 +78,10 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.ConditionVariable; import android.os.INetworkManagementService; -import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.os.test.TestLooper; import android.provider.Settings; import android.security.Credentials; import android.security.KeyStore; @@ -100,6 +92,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; import com.android.server.IpSecService; @@ -223,6 +216,8 @@ public class VpnTest { .thenReturn(mNotificationManager); when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))) .thenReturn(mConnectivityManager); + when(mContext.getSystemServiceName(eq(ConnectivityManager.class))) + .thenReturn(Context.CONNECTIVITY_SERVICE); when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager); when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent)) .thenReturn(Resources.getSystem().getString( @@ -589,7 +584,7 @@ public class VpnTest { } @Test - public void testNotificationShownForAlwaysOnApp() { + public void testNotificationShownForAlwaysOnApp() throws Exception { final UserHandle userHandle = UserHandle.of(primaryUser.id); final Vpn vpn = createVpn(primaryUser.id); setMockedUsers(primaryUser); @@ -617,103 +612,6 @@ public class VpnTest { order.verify(mNotificationManager).cancel(anyString(), anyInt()); } - @Test - public void testCapabilities() { - final Vpn vpn = createVpn(primaryUser.id); - setMockedUsers(primaryUser); - - final Network mobile = new Network(1); - final Network wifi = new Network(2); - - final Map<Network, NetworkCapabilities> networks = new HashMap<>(); - networks.put( - mobile, - new NetworkCapabilities() - .addTransportType(TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .setLinkDownstreamBandwidthKbps(10)); - networks.put( - wifi, - new NetworkCapabilities() - .addTransportType(TRANSPORT_WIFI) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NET_CAPABILITY_NOT_METERED) - .addCapability(NET_CAPABILITY_NOT_ROAMING) - .addCapability(NET_CAPABILITY_NOT_CONGESTED) - .addCapability(NET_CAPABILITY_NOT_SUSPENDED) - .setLinkUpstreamBandwidthKbps(20)); - setMockedNetworks(networks); - - final NetworkCapabilities caps = new NetworkCapabilities(); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, - new Network[] {mobile}, - caps, - false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertTrue(caps.hasTransport(TRANSPORT_CELLULAR)); - assertFalse(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(10, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertFalse(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - - Vpn.applyUnderlyingCapabilities( - mConnectivityManager, - new Network[] {mobile, wifi}, - caps, - false /* isAlwaysMetered */); - assertTrue(caps.hasTransport(TRANSPORT_VPN)); - assertTrue(caps.hasTransport(TRANSPORT_CELLULAR)); - assertTrue(caps.hasTransport(TRANSPORT_WIFI)); - assertEquals(10, caps.getLinkDownstreamBandwidthKbps()); - assertEquals(20, caps.getLinkUpstreamBandwidthKbps()); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED)); - assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED)); - assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)); - } - /** * The profile name should NOT change between releases for backwards compatibility * @@ -1037,7 +935,7 @@ public class VpnTest { when(exception.getErrorType()) .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile)); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent @@ -1048,20 +946,20 @@ public class VpnTest { ikeCb.onClosedExceptionally(exception); verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } @Test public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception { when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any())) .thenThrow(new IllegalArgumentException()); - final Vpn vpn = startLegacyVpn(mVpnProfile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); final NetworkCallback cb = triggerOnAvailableAndGetCallback(); // Wait for createIkeSession() to be called before proceeding in order to ensure consistent // state verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb)); - assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState()); + assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state); } private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) { @@ -1100,8 +998,7 @@ public class VpnTest { // a subsequent CL. } - public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception { - final Vpn vpn = createVpn(primaryUser.id); + private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception { setMockedUsers(primaryUser); // Dummy egress interface @@ -1118,7 +1015,7 @@ public class VpnTest { @Test public void testStartPlatformVpn() throws Exception { - startLegacyVpn(mVpnProfile); + startLegacyVpn(createVpn(primaryUser.id), mVpnProfile); // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in // a subsequent patch. } @@ -1153,7 +1050,7 @@ public class VpnTest { legacyRunnerReady.open(); return new Network(102); }); - final Vpn vpn = startLegacyVpn(profile); + final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile); final TestDeps deps = (TestDeps) vpn.mDeps; try { // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK @@ -1287,8 +1184,13 @@ public class VpnTest { doReturn(UserHandle.of(userId)).when(asUserContext).getUser(); when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt())) .thenReturn(asUserContext); - return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService, + final TestLooper testLooper = new TestLooper(); + final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService, userId, mKeyStore, mSystemServices, mIkev2SessionCreator); + verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat( + provider -> provider.getName().contains("VpnNetworkProvider") + )); + return vpn; } private static void assertBlocked(Vpn vpn, int... uids) { diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 852b1244cd6e..768b4b2c7bfd 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -190,8 +190,11 @@ class LinkCommand : public Command { AddOptionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml if none is present.", &options_.manifest_fixer_options.version_name_default); + AddOptionalFlag("--revision-code", + "Revision code (integer) to inject into the AndroidManifest.xml if none is\n" + "present.", &options_.manifest_fixer_options.revision_code_default); AddOptionalSwitch("--replace-version", - "If --version-code and/or --version-name are specified, these\n" + "If --version-code, --version-name, and/or --revision-code are specified, these\n" "values will replace any value already in the manifest. By\n" "default, nothing is changed if the manifest already defines\n" "these attributes.", diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index c03661ca2366..8abd9dec56be 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -367,6 +367,16 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } } + if (options_.revision_code_default) { + if (options_.replace_version) { + el->RemoveAttribute(xml::kSchemaAndroid, "revisionCode"); + } + if (el->FindAttribute(xml::kSchemaAndroid, "revisionCode") == nullptr) { + el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "revisionCode", + options_.revision_code_default.value()}); + } + } + return true; }); diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h index ec4367b450fb..34ad8d586df1 100644 --- a/tools/aapt2/link/ManifestFixer.h +++ b/tools/aapt2/link/ManifestFixer.h @@ -60,6 +60,10 @@ struct ManifestFixerOptions { // replace_version is set. Maybe<std::string> version_code_major_default; + // The revision code to set if 'android:revisionCode' is not defined in <manifest> or if + // replace_version is set. + Maybe<std::string> revision_code_default; + // The version of the framework being compiled against to set for 'android:compileSdkVersion' in // the <manifest> tag. Maybe<std::string> compile_sdk_version; diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index 781ff7b51637..432f10bdab97 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -445,6 +445,66 @@ TEST_F(ManifestFixerTest, ReplaceVersionNameAndCode) { EXPECT_THAT(attr->value, StrEq("0x20000000")); } +TEST_F(ManifestFixerTest, UseDefaultRevisionCode) { + ManifestFixerOptions options; + options.revision_code_default = std::string("0x10000000"); + + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android" + android:versionCode="0x00000001" />)EOF", + options); + ASSERT_THAT(doc, NotNull()); + + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); + + xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x10000000")); +} + +TEST_F(ManifestFixerTest, DontUseDefaultRevisionCode) { + ManifestFixerOptions options; + options.revision_code_default = std::string("0x10000000"); + + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android" + android:versionCode="0x00000001" + android:revisionCode="0x00000002" />)EOF", + options); + ASSERT_THAT(doc, NotNull()); + + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); + + xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x00000002")); +} + +TEST_F(ManifestFixerTest, ReplaceRevisionCode) { + ManifestFixerOptions options; + options.replace_version = true; + options.revision_code_default = std::string("0x10000000"); + + std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF( + <manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android" + android:versionCode="0x00000001" + android:revisionCode="0x00000002" />)EOF", + options); + ASSERT_THAT(doc, NotNull()); + + xml::Element* manifest_el = doc->root.get(); + ASSERT_THAT(manifest_el, NotNull()); + + xml::Attribute* attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode"); + ASSERT_THAT(attr, NotNull()); + EXPECT_THAT(attr->value, StrEq("0x10000000")); +} + TEST_F(ManifestFixerTest, ReplaceVersionName) { ManifestFixerOptions options; options.replace_version = true; diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt index b90e1bb3e7e7..8cae14a2d040 100644 --- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -145,7 +145,6 @@ class ClassPrinter( } return when { cliArgs.contains("--hidden-$kebabCase") -> true - this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden else -> false } } diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 6a635d0e6181..d9ad649782bb 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.21" +const val CODEGEN_VERSION = "1.0.22" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt index 84faeea36eea..4c1fa6ec40b3 100644 --- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt +++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt @@ -33,13 +33,12 @@ import javax.tools.Diagnostic.Kind import javax.tools.StandardLocation.CLASS_OUTPUT import kotlin.collections.set - /** * The IntDefProcessor is intended to generate a mapping from ints to their respective string * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping. * - * The processor will run when building :frameworks-all and dump all the IntDef mappings found the - * files the make up :frameworks-all as json to outputPath. + * The processor will run when building :framework-minus-apex-intdefs and dump all the IntDef + * mappings found in the files that make up the build target as json to outputPath. */ class IntDefProcessor : AbstractProcessor() { private val outputName = "intDefMapping.json" @@ -72,8 +71,8 @@ class IntDefProcessor : AbstractProcessor() { } private fun generateIntDefMapping( - annotatedElement: TypeElement, - annotationType: TypeElement + annotatedElement: TypeElement, + annotationType: TypeElement ): Map<Int, String> { // LinkedHashMap makes sure ordering is the same as in the code val mapping = LinkedHashMap<Int, String>() @@ -151,8 +150,8 @@ class IntDefProcessor : AbstractProcessor() { companion object { fun serializeTo( - annotationTypeToIntDefMapping: Map<String, IntDefMapping>, - writer: Writer + annotationTypeToIntDefMapping: Map<String, IntDefMapping>, + writer: Writer ) { val indent = " " diff --git a/wifi/Android.bp b/wifi/Android.bp index 52e3840c126e..7a9ca6012be5 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -147,7 +147,7 @@ java_sdk_library { // defaults for tests that need to build against framework-wifi's @hide APIs java_defaults { name: "framework-wifi-test-defaults", - sdk_version: "core_platform", // tests can use @CorePlatformApi's + sdk_version: "core_current", libs: [ // order matters: classes in framework-wifi are resolved before framework, meaning // @hide APIs in framework-wifi are resolved before @SystemApi stubs in framework diff --git a/wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl b/wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl new file mode 100644 index 000000000000..cb359e9b2c1b --- /dev/null +++ b/wifi/aidl-export/android/net/wifi/CoexUnsafeChannel.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +parcelable CoexUnsafeChannel; diff --git a/wifi/api/current.txt b/wifi/api/current.txt index 911f2fbc6afa..ce2b8ca4f2cd 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -14,6 +14,7 @@ package android.net.wifi { field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8 field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa + field public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13; // 0xfffffff3 } public final class ScanResult implements android.os.Parcelable { diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index c4a1766f982c..48d9fd453b05 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -1,8 +1,20 @@ // Signature format: 2.0 package android.net.wifi { + public final class CoexUnsafeChannel implements android.os.Parcelable { + ctor public CoexUnsafeChannel(int, int); + ctor public CoexUnsafeChannel(int, int, int); + method public int getBand(); + method public int getChannel(); + method public int getPowerCapDbm(); + method public boolean isPowerCapAvailable(); + method public void setPowerCapDbm(int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.CoexUnsafeChannel> CREATOR; + } + public abstract class EasyConnectStatusCallback { ctor public EasyConnectStatusCallback(); + method public void onBootstrapUriGenerated(@NonNull String); method public abstract void onConfiguratorSuccess(int); method public abstract void onEnrolleeSuccess(int); method public void onFailure(int); @@ -455,6 +467,8 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>); + method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public int getCoexRestrictions(); + method @NonNull @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public java.util.Set<android.net.wifi.CoexUnsafeChannel> getCoexUnsafeChannels(); method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCountryCode(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public android.net.Network getCurrentNetwork(); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String[] getFactoryMacAddresses(); @@ -475,6 +489,7 @@ package android.net.wifi { method public boolean isVerboseLoggingEnabled(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); + method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void registerCoexCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.CoexCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerNetworkRequestMatchCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback); @@ -486,6 +501,7 @@ package android.net.wifi { method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) public void setCoexUnsafeChannels(@NonNull java.util.Set<android.net.wifi.CoexUnsafeChannel>, int); method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setPasspointMeteredOverride(@NonNull String, int); @@ -497,6 +513,7 @@ package android.net.wifi { method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeResponder(@Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback); method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback); @@ -505,6 +522,7 @@ package android.net.wifi { method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession(); method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp(); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopTemporarilyDisablingAllNonCarrierMergedWifi(); + method @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) public void unregisterCoexCallback(@NonNull android.net.wifi.WifiManager.CoexCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback); method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback); @@ -518,11 +536,22 @@ package android.net.wifi { field public static final int CHANGE_REASON_ADDED = 0; // 0x0 field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2 field public static final int CHANGE_REASON_REMOVED = 1; // 0x1 + field public static final int COEX_RESTRICTION_SOFTAP = 2; // 0x2 + field public static final int COEX_RESTRICTION_WIFI_AWARE = 4; // 0x4 + field public static final int COEX_RESTRICTION_WIFI_DIRECT = 1; // 0x1 field public static final String CONFIGURED_NETWORKS_CHANGED_ACTION = "android.net.wifi.CONFIGURED_NETWORKS_CHANGE"; field public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; // 0x1 field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2 field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3 field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; // 0x3 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; // 0x4 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; // 0x5 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; // 0x0 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; // 0x1 + field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; // 0x2 + field public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; // 0x28 field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1 field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0 field public static final String EXTRA_CHANGE_REASON = "changeReason"; @@ -565,6 +594,11 @@ package android.net.wifi { method public void onSuccess(); } + public abstract static class WifiManager.CoexCallback { + ctor public WifiManager.CoexCallback(); + method public abstract void onCoexUnsafeChannelsChanged(); + } + public static interface WifiManager.NetworkRequestMatchCallback { method public default void onAbort(); method public default void onMatch(@NonNull java.util.List<android.net.wifi.ScanResult>); diff --git a/wifi/java/android/net/wifi/CoexUnsafeChannel.java b/wifi/java/android/net/wifi/CoexUnsafeChannel.java new file mode 100644 index 000000000000..3f9efa020d05 --- /dev/null +++ b/wifi/java/android/net/wifi/CoexUnsafeChannel.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; +import static android.net.wifi.WifiScanner.WIFI_BAND_5_GHZ; +import static android.net.wifi.WifiScanner.WIFI_BAND_6_GHZ; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Data structure class representing a Wi-Fi channel that would cause interference to/receive + * interference from the active cellular channels and should be avoided. + * + * If {@link #isPowerCapAvailable()} is {@code true}, then a valid power cap value is available + * through {@link #getPowerCapDbm()} to be used if this channel cannot be avoided. If {@code false}, + * then {@link #getPowerCapDbm()} throws an IllegalStateException and the channel will not need to + * cap its power. + * + * @hide + */ +@SystemApi +public final class CoexUnsafeChannel implements Parcelable { + private @WifiAnnotations.WifiBandBasic int mBand; + private int mChannel; + private boolean mIsPowerCapAvailable = false; + private int mPowerCapDbm; + + /** + * Constructor for a CoexUnsafeChannel with no power cap specified. + * @param band One of {@link WifiAnnotations.WifiBandBasic} + * @param channel Channel number + */ + public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel) { + mBand = band; + mChannel = channel; + } + + /** + * Constructor for a CoexUnsafeChannel with power cap specified. + * @param band One of {@link WifiAnnotations.WifiBandBasic} + * @param channel Channel number + * @param powerCapDbm Power cap in dBm + */ + public CoexUnsafeChannel(@WifiAnnotations.WifiBandBasic int band, int channel, + int powerCapDbm) { + mBand = band; + mChannel = channel; + setPowerCapDbm(powerCapDbm); + } + + /** Returns the Wi-Fi band of this channel as one of {@link WifiAnnotations.WifiBandBasic} */ + public @WifiAnnotations.WifiBandBasic int getBand() { + return mBand; + } + + /** Returns the channel number of this channel. */ + public int getChannel() { + return mChannel; + } + + /** Returns {@code true} if {@link #getPowerCapDbm()} is a valid value, else {@code false} */ + public boolean isPowerCapAvailable() { + return mIsPowerCapAvailable; + } + + /** + * Returns the power cap of this channel in dBm. Throws IllegalStateException if + * {@link #isPowerCapAvailable()} is {@code false}. + */ + public int getPowerCapDbm() { + if (!mIsPowerCapAvailable) { + throw new IllegalStateException("getPowerCapDbm called but power cap is unavailable"); + } + return mPowerCapDbm; + } + + /** Set the power cap of this channel. */ + public void setPowerCapDbm(int powerCapDbm) { + mIsPowerCapAvailable = true; + mPowerCapDbm = powerCapDbm; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CoexUnsafeChannel that = (CoexUnsafeChannel) o; + return mBand == that.mBand + && mChannel == that.mChannel + && mIsPowerCapAvailable == that.mIsPowerCapAvailable + && mPowerCapDbm == that.mPowerCapDbm; + } + + @Override + public int hashCode() { + return Objects.hash(mBand, mChannel, mIsPowerCapAvailable, mPowerCapDbm); + } + + @Override + public String toString() { + StringBuilder sj = new StringBuilder("CoexUnsafeChannel{"); + sj.append(mChannel); + sj.append(", "); + if (mBand == WIFI_BAND_24_GHZ) { + sj.append("2.4GHz"); + } else if (mBand == WIFI_BAND_5_GHZ) { + sj.append("5GHz"); + } else if (mBand == WIFI_BAND_6_GHZ) { + sj.append("6GHz"); + } else { + sj.append("UNKNOWN BAND"); + } + if (mIsPowerCapAvailable) { + sj.append(", ").append(mPowerCapDbm).append("dBm"); + } + sj.append('}'); + return sj.toString(); + } + + /** Implement the Parcelable interface {@hide} */ + @Override + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mBand); + dest.writeInt(mChannel); + dest.writeBoolean(mIsPowerCapAvailable); + if (mIsPowerCapAvailable) { + dest.writeInt(mPowerCapDbm); + } + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<CoexUnsafeChannel> CREATOR = + new Creator<CoexUnsafeChannel>() { + public CoexUnsafeChannel createFromParcel(Parcel in) { + final int band = in.readInt(); + final int channel = in.readInt(); + final boolean isPowerCapAvailable = in.readBoolean(); + if (isPowerCapAvailable) { + final int powerCapDbm = in.readInt(); + return new CoexUnsafeChannel(band, channel, powerCapDbm); + } + return new CoexUnsafeChannel(band, channel); + } + + public CoexUnsafeChannel[] newArray(int size) { + return new CoexUnsafeChannel[size]; + } + }; +} diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java index 6c2e6ddf5dd2..ee7025594bc6 100644 --- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java +++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java @@ -161,6 +161,11 @@ public abstract class EasyConnectStatusCallback { */ public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12; + /** + * Easy Connect Failure event: System failed to generate DPP URI. + */ + public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13; + /** @hide */ @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = { EASY_CONNECT_EVENT_FAILURE_INVALID_URI, @@ -175,6 +180,7 @@ public abstract class EasyConnectStatusCallback { EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK, EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION, EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION, + EASY_CONNECT_EVENT_FAILURE_URI_GENERATION, }) @Retention(RetentionPolicy.SOURCE) public @interface EasyConnectFailureStatusCode { @@ -264,4 +270,17 @@ public abstract class EasyConnectStatusCallback { */ @SystemApi public abstract void onProgress(@EasyConnectProgressStatusCode int code); + + /** + * Called when local Easy Connect Responder successfully generates a DPP URI from + * the supplicant. This callback is the first successful outcome + * of a Easy Connect Responder flow starting with + * {@link WifiManager#startEasyConnectAsEnrolleeResponder(String, int, Executor, + * EasyConnectStatusCallback)} . + * + * @param uri DPP URI from the supplicant. + * @hide + */ + @SystemApi + public void onBootstrapUriGenerated(@NonNull String uri) {}; } diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/wifi/java/android/net/wifi/ICoexCallback.aidl index 45e4c69102f0..89e4c4b93013 100644 --- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl +++ b/wifi/java/android/net/wifi/ICoexCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,11 @@ package android.net.wifi; -/** @hide */ -parcelable WifiApiServiceInfo { - String name; - IBinder binder; +/** + * Interface for Wi-Fi/cellular coex callback. + * @hide + */ +oneway interface ICoexCallback +{ + void onCoexUnsafeChannelsChanged(); } diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl index d7a958a5b4b1..dcbe8468de9e 100644 --- a/wifi/java/android/net/wifi/IDppCallback.aidl +++ b/wifi/java/android/net/wifi/IDppCallback.aidl @@ -45,4 +45,10 @@ oneway interface IDppCallback * to show progress. */ void onProgress(int status); + + /** + * Called when local DPP Responder successfully generates a URI. + */ + void onBootstrapUriGenerated(String uri); + } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index cc864eafcff1..6dee751f5894 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -24,7 +24,9 @@ import android.net.wifi.hotspot2.IProvisioningCallback; import android.net.DhcpInfo; import android.net.Network; +import android.net.wifi.CoexUnsafeChannel; import android.net.wifi.IActionListener; +import android.net.wifi.ICoexCallback; import android.net.wifi.IDppCallback; import android.net.wifi.ILocalOnlyHotspotCallback; import android.net.wifi.INetworkRequestMatchCallback; @@ -144,6 +146,16 @@ interface IWifiManager void updateInterfaceIpState(String ifaceName, int mode); + void setCoexUnsafeChannels(in List<CoexUnsafeChannel> unsafeChannels, int mandatoryRestrictions); + + List<CoexUnsafeChannel> getCoexUnsafeChannels(); + + int getCoexRestrictions(); + + void registerCoexCallback(in ICoexCallback callback); + + void unregisterCoexCallback(in ICoexCallback callback); + boolean startSoftAp(in WifiConfiguration wifiConfig, String packageName); boolean startTetheredHotspot(in SoftApConfiguration softApConfig, String packageName); @@ -235,6 +247,9 @@ interface IWifiManager void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri, in IDppCallback callback); + void startDppAsEnrolleeResponder(in IBinder binder, in String deviceInfo, int curve, + in IDppCallback callback); + void stopDppSession(); void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index da7c9c055d65..b8fa1e18ed28 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -72,6 +72,7 @@ import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; @@ -3110,6 +3111,238 @@ public class WifiManager { } } + /* Wi-Fi/Cellular Coex */ + + /** + * Mandatory coex restriction flag for Wi-Fi Direct. + * + * @see #setCoexUnsafeChannels(Set, int) + * + * @hide + */ + @SystemApi + public static final int COEX_RESTRICTION_WIFI_DIRECT = 0x1 << 0; + + /** + * Mandatory coex restriction flag for SoftAP + * + * @see #setCoexUnsafeChannels(Set, int) + * + * @hide + */ + @SystemApi + public static final int COEX_RESTRICTION_SOFTAP = 0x1 << 1; + + /** + * Mandatory coex restriction flag for Wi-Fi Aware. + * + * @see #setCoexUnsafeChannels(Set, int) + * + * @hide + */ + @SystemApi + public static final int COEX_RESTRICTION_WIFI_AWARE = 0x1 << 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, prefix = {"COEX_RESTRICTION_"}, value = { + COEX_RESTRICTION_WIFI_DIRECT, + COEX_RESTRICTION_SOFTAP, + COEX_RESTRICTION_WIFI_AWARE + }) + public @interface CoexRestriction {} + + /** + * Specify the set of {@link CoexUnsafeChannel} to propagate through the framework for + * Wi-Fi/Cellular coex channel avoidance if the default algorithm is disabled via overlay + * (i.e. config_wifiCoexDefaultAlgorithmEnabled = false). Otherwise do nothing. + * + * @param unsafeChannels Set of {@link CoexUnsafeChannel} to avoid. + * @param restrictions Bitmap of {@link CoexRestriction} specifying the mandatory restricted + * uses of the specified channels. If any restrictions are set, then the + * supplied CoexUnsafeChannels will be completely avoided for the + * specified modes, rather than be avoided with best effort. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS) + public void setCoexUnsafeChannels(@NonNull Set<CoexUnsafeChannel> unsafeChannels, + int restrictions) { + if (unsafeChannels == null) { + throw new IllegalArgumentException("unsafeChannels must not be null"); + } + try { + mService.setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the set of current {@link CoexUnsafeChannel} being used for Wi-Fi/Cellular coex + * channel avoidance. + * + * This returns the set calculated by the default algorithm if + * config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the set supplied + * in {@link #setCoexUnsafeChannels(Set, int)}. + * + * If any {@link CoexRestriction} flags are set in {@link #getCoexRestrictions()}, then the + * CoexUnsafeChannels should be totally avoided (i.e. not best effort) for the Wi-Fi modes + * specified by the flags. + * + * @return Set of current CoexUnsafeChannels. + * + * @hide + */ + @NonNull + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) + public Set<CoexUnsafeChannel> getCoexUnsafeChannels() { + try { + return new HashSet<>(mService.getCoexUnsafeChannels()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the current coex restrictions being used for Wi-Fi/Cellular coex + * channel avoidance. + * + * This returns the restrictions calculated by the default algorithm if + * config_wifiCoexDefaultAlgorithmEnabled is {@code true}. Otherwise, returns the value supplied + * in {@link #setCoexUnsafeChannels(Set, int)}. + * + * @return int containing a bitwise-OR combination of {@link CoexRestriction}. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) + public int getCoexRestrictions() { + try { + return mService.getCoexRestrictions(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Registers a CoexCallback to listen on the current CoexUnsafeChannels and restrictions being + * used for Wi-Fi/cellular coex channel avoidance. + * @param executor Executor to execute listener callback on + * @param callback CoexCallback to register + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) + public void registerCoexCallback( + @NonNull @CallbackExecutor Executor executor, @NonNull CoexCallback callback) { + if (executor == null) throw new IllegalArgumentException("executor must not be null"); + if (callback == null) throw new IllegalArgumentException("callback must not be null"); + CoexCallback.CoexCallbackProxy proxy = callback.getProxy(); + proxy.initProxy(executor, callback); + try { + mService.registerCoexCallback(proxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Unregisters a CoexCallback from listening on the current CoexUnsafeChannels and restrictions + * being used for Wi-Fi/cellular coex channel avoidance. + * @param callback CoexCallback to unregister + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS) + public void unregisterCoexCallback(@NonNull CoexCallback callback) { + if (callback == null) throw new IllegalArgumentException("callback must not be null"); + CoexCallback.CoexCallbackProxy proxy = callback.getProxy(); + try { + mService.unregisterCoexCallback(proxy); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } finally { + proxy.cleanUpProxy(); + } + } + + /** + * Abstract callback class for applications to receive updates about current CoexUnsafeChannels + * for Wi-Fi/Cellular coex channel avoidance. + * + * @hide + */ + @SystemApi + public abstract static class CoexCallback { + private final CoexCallbackProxy mCoexCallbackProxy; + + public CoexCallback() { + mCoexCallbackProxy = new CoexCallbackProxy(); + } + + /*package*/ @NonNull + CoexCallbackProxy getProxy() { + return mCoexCallbackProxy; + } + + /** + * Indicates that the current CoexUnsafeChannels or restrictions have changed. + * Clients should call {@link #getCoexUnsafeChannels()} and {@link #getCoexRestrictions()} + * to get the updated values. + */ + public abstract void onCoexUnsafeChannelsChanged(); + + /** + * Callback proxy for CoexCallback objects. + */ + private static class CoexCallbackProxy extends ICoexCallback.Stub { + private final Object mLock = new Object(); + @Nullable @GuardedBy("mLock") private Executor mExecutor; + @Nullable @GuardedBy("mLock") private CoexCallback mCallback; + + CoexCallbackProxy() { + mExecutor = null; + mCallback = null; + } + + /*package*/ void initProxy(@NonNull Executor executor, + @NonNull CoexCallback callback) { + synchronized (mLock) { + mExecutor = executor; + mCallback = callback; + } + } + + /*package*/ void cleanUpProxy() { + synchronized (mLock) { + mExecutor = null; + mCallback = null; + } + } + + @Override + public void onCoexUnsafeChannelsChanged() { + Executor executor; + CoexCallback callback; + synchronized (mLock) { + executor = mExecutor; + callback = mCallback; + } + if (executor == null || callback == null) { + return; + } + Binder.clearCallingIdentity(); + executor.execute(callback::onCoexUnsafeChannelsChanged); + } + } + } + /** * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration. * Note that starting Soft AP mode may disable station mode operation if the device does not @@ -5551,6 +5784,89 @@ public class WifiManager { } /** + * Easy Connect Device information maximum allowed length. + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; + + /** + * Easy Connect Cryptography Curve name: prime256v1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; + + /** + * Easy Connect Cryptography Curve name: secp384r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; + + /** + * Easy Connect Cryptography Curve name: secp521r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP256r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP384r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; + + + /** + * Easy Connect Cryptography Curve name: brainpoolP512r1 + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; + + /** + * Easy Connect Cryptography Curve name: default + * This allows framework to choose manadatory curve prime256v1. + * + * @hide + */ + @SystemApi + public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT = + EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1; + + /** @hide */ + @IntDef(prefix = {"EASY_CONNECT_CRYPTOGRAPHY_CURVE_"}, value = { + EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1, + EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EasyConnectCryptographyCurve { + } + + /** * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of * the specified network using the Easy Connect protocol on an encrypted link. @@ -5606,6 +5922,52 @@ public class WifiManager { } /** + * Start Easy Connect (DPP) in Enrollee-Responder role. + * The device will: + * 1. Generate a DPP bootstrap URI and return it using the + * {@link EasyConnectStatusCallback#onBootstrapUriGenerated(String)} method. + * 2. Start DPP as a Responder, waiting for an Initiator device to start the DPP + * authentication process. + * The caller should use the URI provided in step #1, for instance display it as a QR code + * or communicate it in some other way to the initiator device. + * + * @param deviceInfo Device specific information to add to the DPP URI. This field allows + * the users of the configurators to identify the device. + * Optional - if not provided or in case of an empty string, + * Info field (I:) will be skipped in the generated DPP URI. + * Allowed Range of ASCII characters in deviceInfo - %x20-7E. + * semicolon and space are not allowed. + * Due to the limitation of maximum allowed characters in QR code, + * framework limits to a max of + * {@link #EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH} characters in + * deviceInfo. + * Violation of these rules will result in an exception. + * @param curve Elliptic curve cryptography used to generate DPP + * public/private key pair. If application is not interested in a + * specific curve, choose default curve + * {@link #EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT}. + * @param callback Callback for status updates + * @param executor The Executor on which to run the callback. + * @hide + */ + @SystemApi + @RequiresPermission(anyOf = { + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_SETUP_WIZARD}) + public void startEasyConnectAsEnrolleeResponder(@Nullable String deviceInfo, + @EasyConnectCryptographyCurve int curve, + @NonNull @CallbackExecutor Executor executor, + @NonNull EasyConnectStatusCallback callback) { + Binder binder = new Binder(); + try { + mService.startDppAsEnrolleeResponder(binder, deviceInfo, curve, + new EasyConnectCallbackProxy(executor, callback)); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Stop or abort a current Easy Connect (DPP) session. This call, once processed, will * terminate any ongoing transaction, and clean up all associated resources. Caller should not * expect any callbacks once this call is made. However, due to the asynchronous nature of @@ -5679,6 +6041,15 @@ public class WifiManager { mEasyConnectStatusCallback.onProgress(status); }); } + + @Override + public void onBootstrapUriGenerated(String uri) { + Log.d(TAG, "Easy Connect onBootstrapUriGenerated callback"); + Binder.clearCallingIdentity(); + mExecutor.execute(() -> { + mEasyConnectStatusCallback.onBootstrapUriGenerated(uri); + }); + } } /** @@ -5853,7 +6224,6 @@ public class WifiManager { executor.execute(callback::onScanResultsAvailable); } } - } /** diff --git a/wifi/tests/Android.bp b/wifi/tests/Android.bp index 7272e146f4f8..7eba0a73be31 100644 --- a/wifi/tests/Android.bp +++ b/wifi/tests/Android.bp @@ -20,6 +20,9 @@ android_test { defaults: ["framework-wifi-test-defaults"], + min_sdk_version: "30", + target_sdk_version: "30", + srcs: ["**/*.java"], jacoco: { diff --git a/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java b/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java new file mode 100644 index 000000000000..320f25e715fe --- /dev/null +++ b/wifi/tests/src/android/net/wifi/CoexUnsafeChannelTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.CoexUnsafeChannel}. + */ +@SmallTest +public class CoexUnsafeChannelTest { + /** + * Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns false if no cap is set. + */ + @Test + public void testIsPowerCapAvailable_noPowerCap_returnsFalse() { + CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6); + + assertThat(unsafeChannel.isPowerCapAvailable()).isFalse(); + } + + /** + * Verifies {@link CoexUnsafeChannel#isPowerCapAvailable()} returns true if a cap is set, and + * {@link CoexUnsafeChannel#getPowerCapDbm()} returns the set value. + */ + @Test + public void testIsPowerCapAvailable_powerCapSet_returnsTrue() { + final int powerCapDbm = -50; + CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6); + + unsafeChannel.setPowerCapDbm(powerCapDbm); + + assertThat(unsafeChannel.isPowerCapAvailable()).isTrue(); + assertThat(unsafeChannel.getPowerCapDbm()).isEqualTo(powerCapDbm); + } + + /** + * Verifies {@link CoexUnsafeChannel#getPowerCapDbm()} throws an IllegalStateException if + * {@link CoexUnsafeChannel#isPowerCapAvailable()} is {@code false}. + */ + @Test(expected = IllegalStateException.class) + public void testGetPowerCap_powerCapUnavailable_throwsException() { + CoexUnsafeChannel unsafeChannel = new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6); + + unsafeChannel.getPowerCapDbm(); + } + + /** + * Verify parcel read/write for CoexUnsafeChannel with or without power cap. + */ + @Test + public void testParcelReadWrite_withOrWithoutCap_readEqualsWritten() throws Exception { + CoexUnsafeChannel writeUnsafeChannelNoCap = + new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6); + CoexUnsafeChannel writeUnsafeChannelCapped = + new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6, -50); + + CoexUnsafeChannel readUnsafeChannelNoCap = parcelReadWrite(writeUnsafeChannelNoCap); + CoexUnsafeChannel readUnsafeChannelCapped = parcelReadWrite(writeUnsafeChannelCapped); + + assertThat(writeUnsafeChannelNoCap).isEqualTo(readUnsafeChannelNoCap); + assertThat(writeUnsafeChannelCapped).isEqualTo(readUnsafeChannelCapped); + } + + /** + * Write the provided {@link CoexUnsafeChannel} to a parcel and deserialize it. + */ + private static CoexUnsafeChannel parcelReadWrite(CoexUnsafeChannel writeResult) + throws Exception { + Parcel parcel = Parcel.obtain(); + writeResult.writeToParcel(parcel, 0); + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + return CoexUnsafeChannel.CREATOR.createFromParcel(parcel); + } +} diff --git a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java index b10141434b0b..cf37b78b889a 100644 --- a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java +++ b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java @@ -19,6 +19,7 @@ package android.net.wifi; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import android.annotation.NonNull; import android.util.SparseArray; import androidx.test.filters.SmallTest; @@ -52,6 +53,11 @@ public class EasyConnectStatusCallbackTest { mOnFailureR1EventReceived = true; mLastCode = code; } + + @Override + public void onBootstrapUriGenerated(@NonNull String uri) { + + } }; private boolean mOnFailureR1EventReceived; private int mLastCode; diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index aefebbcec607..39f6f57b05b3 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -19,6 +19,9 @@ package android.net.wifi; import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED; import static android.net.wifi.WifiManager.ActionListener; import static android.net.wifi.WifiManager.BUSY; +import static android.net.wifi.WifiManager.COEX_RESTRICTION_SOFTAP; +import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_AWARE; +import static android.net.wifi.WifiManager.COEX_RESTRICTION_WIFI_DIRECT; import static android.net.wifi.WifiManager.ERROR; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC; import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE; @@ -43,6 +46,7 @@ import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE; import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B; import static android.net.wifi.WifiManager.WpsCallback; +import static android.net.wifi.WifiScanner.WIFI_BAND_24_GHZ; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -74,6 +78,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.net.DhcpInfo; import android.net.MacAddress; +import android.net.wifi.WifiManager.CoexCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotCallback; import android.net.wifi.WifiManager.LocalOnlyHotspotObserver; import android.net.wifi.WifiManager.LocalOnlyHotspotReservation; @@ -108,9 +113,11 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -151,6 +158,7 @@ public class WifiManagerTest { private WifiManager mWifiManager; private WifiNetworkSuggestion mWifiNetworkSuggestion; private ScanResultsCallback mScanResultsCallback; + private CoexCallback mCoexCallback; private WifiActivityEnergyInfo mWifiActivityEnergyInfo; /** @@ -214,10 +222,149 @@ public class WifiManagerTest { mRunnable.run(); } }; + mCoexCallback = new CoexCallback() { + @Override + public void onCoexUnsafeChannelsChanged() { + mRunnable.run(); + } + }; mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0); } /** + * Check the call to setCoexUnsafeChannels calls WifiServiceImpl to setCoexUnsafeChannels with + * the provided CoexUnsafeChannels and restrictions bitmask. + */ + @Test + public void testSetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception { + Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>(); + int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP + | COEX_RESTRICTION_WIFI_AWARE; + + mWifiManager.setCoexUnsafeChannels(unsafeChannels, restrictions); + + verify(mWifiService).setCoexUnsafeChannels(new ArrayList<>(unsafeChannels), restrictions); + } + + /** + * Verify an IllegalArgumentException if passed a null value for unsafeChannels. + */ + @Test + public void testSetCoexUnsafeChannelsThrowsIllegalArgumentExceptionOnNullUnsafeChannels() { + try { + mWifiManager.setCoexUnsafeChannels(null, 0); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + + /** + * Check the call to getCoexUnsafeChannels calls WifiServiceImpl to return the values from + * getCoexUnsafeChannels. + */ + @Test + public void testGetCoexUnsafeChannelsGoesToWifiServiceImpl() throws Exception { + Set<CoexUnsafeChannel> unsafeChannels = new HashSet<>(); + unsafeChannels.add(new CoexUnsafeChannel(WIFI_BAND_24_GHZ, 6)); + when(mWifiService.getCoexUnsafeChannels()).thenReturn(new ArrayList<>(unsafeChannels)); + + assertEquals(mWifiManager.getCoexUnsafeChannels(), unsafeChannels); + } + + /** + * Verify call to getCoexRestrictions calls WifiServiceImpl to return the value from + * getCoexRestrictions. + */ + @Test + public void testGetCoexRestrictionsGoesToWifiServiceImpl() throws Exception { + int restrictions = COEX_RESTRICTION_WIFI_DIRECT | COEX_RESTRICTION_SOFTAP + | COEX_RESTRICTION_WIFI_AWARE; + when(mWifiService.getCoexRestrictions()).thenReturn(restrictions); + + assertEquals(mWifiService.getCoexRestrictions(), restrictions); + } + + + /** + * Verify an IllegalArgumentException is thrown if callback is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRegisterCoexCallbackWithNullCallback() throws Exception { + mWifiManager.registerCoexCallback(mExecutor, null); + } + + /** + * Verify an IllegalArgumentException is thrown if executor is not provided. + */ + @Test(expected = IllegalArgumentException.class) + public void testRegisterCoexCallbackWithNullExecutor() throws Exception { + mWifiManager.registerCoexCallback(null, mCoexCallback); + } + + /** + * Verify client provided callback is being called to the right callback. + */ + @Test + public void testAddCoexCallbackAndReceiveEvent() throws Exception { + ArgumentCaptor<ICoexCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ICoexCallback.Stub.class); + mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback); + verify(mWifiService).registerCoexCallback(callbackCaptor.capture()); + callbackCaptor.getValue().onCoexUnsafeChannelsChanged(); + verify(mRunnable).run(); + } + + /** + * Verify client provided callback is being called to the right executor. + */ + @Test + public void testRegisterCoexCallbackWithTheTargetExecutor() throws Exception { + ArgumentCaptor<ICoexCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ICoexCallback.Stub.class); + mWifiManager.registerCoexCallback(mExecutor, mCoexCallback); + verify(mWifiService).registerCoexCallback(callbackCaptor.capture()); + mWifiManager.registerCoexCallback(mAnotherExecutor, mCoexCallback); + callbackCaptor.getValue().onCoexUnsafeChannelsChanged(); + verify(mExecutor, never()).execute(any(Runnable.class)); + verify(mAnotherExecutor).execute(any(Runnable.class)); + } + + /** + * Verify client register unregister then register again, to ensure callback still works. + */ + @Test + public void testRegisterUnregisterThenRegisterAgainWithCoexCallback() throws Exception { + ArgumentCaptor<ICoexCallback.Stub> callbackCaptor = + ArgumentCaptor.forClass(ICoexCallback.Stub.class); + mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback); + verify(mWifiService).registerCoexCallback(callbackCaptor.capture()); + mWifiManager.unregisterCoexCallback(mCoexCallback); + callbackCaptor.getValue().onCoexUnsafeChannelsChanged(); + verify(mRunnable, never()).run(); + mWifiManager.registerCoexCallback(new SynchronousExecutor(), mCoexCallback); + callbackCaptor.getValue().onCoexUnsafeChannelsChanged(); + verify(mRunnable).run(); + } + + /** + * Verify client unregisterCoexCallback. + */ + @Test + public void testUnregisterCoexCallback() throws Exception { + mWifiManager.unregisterCoexCallback(mCoexCallback); + verify(mWifiService).unregisterCoexCallback(any()); + } + + /** + * Verify client unregisterCoexCallback with null callback will cause an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testUnregisterCoexCallbackWithNullCallback() throws Exception { + mWifiManager.unregisterCoexCallback(null); + } + + + /** * Check the call to startSoftAp calls WifiService to startSoftAp with the provided * WifiConfiguration. Verify that the return value is propagated to the caller. */ |