summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java95
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl11
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java36
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/SearchResults.java89
-rw-r--r--apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java127
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java27
-rw-r--r--api/current.txt27
-rw-r--r--api/module-app-current.txt8
-rwxr-xr-xapi/system-current.txt57
-rw-r--r--core/java/android/app/AppOpsManager.java9
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/ResourcesManager.java51
-rw-r--r--core/java/android/app/StatsManager.java77
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java44
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/app/servertransaction/TransactionExecutorHelper.java12
-rw-r--r--core/java/android/content/Context.java8
-rw-r--r--core/java/android/hardware/display/DisplayManager.java7
-rw-r--r--core/java/android/os/IUserManager.aidl2
-rw-r--r--core/java/android/os/PowerManager.java8
-rw-r--r--core/java/android/os/UserManager.java51
-rw-r--r--core/java/android/os/storage/StorageManager.java9
-rw-r--r--core/java/android/provider/Settings.java79
-rw-r--r--core/java/android/service/notification/StatusBarNotification.java18
-rw-r--r--core/java/android/view/Display.java2
-rw-r--r--core/java/android/view/SurfaceControl.java11
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java23
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnection.aidl75
-rw-r--r--core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl44
-rw-r--r--core/java/android/view/textclassifier/TextLinks.java26
-rw-r--r--core/java/android/view/textclassifier/TextLinksParams.java2
-rw-r--r--core/java/com/android/internal/accessibility/AccessibilityShortcutController.java4
-rw-r--r--core/jni/android_media_AudioTrack.cpp221
-rw-r--r--core/jni/android_view_SurfaceControl.cpp9
-rw-r--r--core/proto/android/stats/devicepolicy/device_policy_enums.proto1
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java66
-rw-r--r--core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java6
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java12
-rw-r--r--media/java/android/media/AudioTrack.java193
-rw-r--r--media/java/android/media/IMediaRoute2Provider.aidl4
-rw-r--r--media/java/android/media/IMediaRouter2Manager.aidl4
-rw-r--r--media/java/android/media/IMediaRouterService.aidl12
-rw-r--r--media/java/android/media/MediaRoute2ProviderService.java20
-rw-r--r--media/java/android/media/MediaRouter2.java55
-rw-r--r--media/java/android/media/MediaRouter2Manager.java365
-rw-r--r--media/java/android/media/tv/tuner/Descrambler.java4
-rw-r--r--media/java/android/media/tv/tuner/Lnb.java40
-rw-r--r--media/java/android/media/tv/tuner/LnbCallback.java4
-rw-r--r--media/java/android/media/tv/tuner/Tuner.java130
-rw-r--r--media/java/android/media/tv/tuner/TunerConstants.java98
-rw-r--r--media/java/android/media/tv/tuner/TunerUtils.java78
-rw-r--r--media/java/android/media/tv/tuner/dvr/Dvr.java36
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrCallback.java7
-rw-r--r--media/java/android/media/tv/tuner/dvr/DvrSettings.java9
-rw-r--r--media/java/android/media/tv/tuner/filter/AvSettings.java8
-rw-r--r--media/java/android/media/tv/tuner/filter/DownloadSettings.java6
-rw-r--r--media/java/android/media/tv/tuner/filter/Filter.java135
-rw-r--r--media/java/android/media/tv/tuner/filter/FilterConfiguration.java5
-rw-r--r--media/java/android/media/tv/tuner/filter/MediaEvent.java33
-rw-r--r--media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java4
-rw-r--r--media/java/android/media/tv/tuner/filter/PesSettings.java8
-rw-r--r--media/java/android/media/tv/tuner/filter/RecordSettings.java5
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettings.java3
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java3
-rw-r--r--media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java3
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java55
-rw-r--r--media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java20
-rw-r--r--media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java26
-rw-r--r--media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java14
-rw-r--r--media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java48
-rw-r--r--media/java/android/media/tv/tuner/frontend/ScanCallback.java52
-rw-r--r--media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java30
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java73
-rw-r--r--media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java114
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SystemUI/res/layout/tv_audio_recording_indicator.xml48
-rw-r--r--packages/SystemUI/res/values/config.xml9
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java181
-rw-r--r--proto/src/system_messages.proto4
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java38
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java13
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java110
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java98
-rw-r--r--services/backup/java/com/android/server/backup/UserBackupManagerService.java69
-rw-r--r--services/backup/java/com/android/server/backup/internal/BackupHandler.java19
-rw-r--r--services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java110
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java60
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java9
-rw-r--r--services/core/java/com/android/server/content/ContentService.java30
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java50
-rw-r--r--services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java26
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2Provider.java4
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java12
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java205
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java32
-rw-r--r--services/core/java/com/android/server/media/SystemMediaRoute2Provider.java4
-rw-r--r--services/core/java/com/android/server/notification/NotificationChannelExtractor.java11
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java10
-rw-r--r--services/core/java/com/android/server/notification/PreferencesHelper.java15
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java29
-rw-r--r--services/core/java/com/android/server/power/Notifier.java17
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java28
-rw-r--r--services/core/java/com/android/server/stats/pull/StatsPullAtomService.java85
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java10
-rw-r--r--services/core/java/com/android/server/wm/ActivityStackSupervisor.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainer.java7
-rw-r--r--services/core/java/com/android/server/wm/ConfigurationContainerListener.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java75
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java2
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java35
-rw-r--r--services/java/com/android/server/SystemServer.java23
-rw-r--r--services/robotests/backup/Android.bp2
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java83
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java25
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java67
-rw-r--r--services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java134
-rw-r--r--services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java52
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java61
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java8
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java81
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java159
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java31
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java21
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java3
-rw-r--r--tests/net/integration/util/com/android/server/NetworkAgentWrapper.java8
-rw-r--r--wifi/java/android/net/wifi/IScoreChangeCallback.aidl (renamed from media/java/android/media/tv/tuner/frontend/FrontendCallback.java)13
-rw-r--r--wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl33
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl5
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java182
-rw-r--r--wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java160
-rw-r--r--wifi/java/android/net/wifi/wificond/WifiCondManager.java16
-rw-r--r--wifi/java/com/android/server/wifi/BaseWifiService.java12
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java61
-rw-r--r--wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java59
-rw-r--r--wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java21
159 files changed, 4987 insertions, 1266 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
index 83195dc73db6..0aa685d6e1bf 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.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.
@@ -25,9 +25,12 @@ import android.os.RemoteException;
import com.android.internal.infra.AndroidFuture;
import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.protobuf.InvalidProtocolBufferException;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -127,4 +130,94 @@ public class AppSearchManager {
// TODO(b/147614371) Fix error report for multiple documents.
future.whenCompleteAsync((noop, err) -> callback.accept(err), executor);
}
+
+ /**
+ * This method searches for documents based on a given query string. It also accepts
+ * specifications regarding how to search and format the results.
+ *
+ *<p>Currently we support following features in the raw query format:
+ * <ul>
+ * <li>AND
+ * 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
+ * OR joins (e.g. “match documents that have either the term ‘dog’ or
+ * ‘cat’”).
+ * Example: dog OR puppy
+ * <li>Exclusion
+ * Exclude a term (e.g. “match documents that do
+ * not have the term ‘dog’”).
+ * Example: -dog excludes the term ‘dog’
+ * <li>Grouping terms
+ * 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
+ * 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
+ * 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> It is strongly recommended to use Jetpack APIs.
+ *
+ * @param queryExpression Query String to search.
+ * @param searchSpec Spec for setting filters, raw query etc.
+ * @param executor Executor on which to invoke the callback.
+ * @param callback Callback to receive errors resulting from the query operation. If the
+ * operation succeeds, the callback will be invoked with {@code null}.
+ * @hide
+ */
+ @NonNull
+ public void query(
+ @NonNull String queryExpression,
+ @NonNull SearchSpec searchSpec,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull BiConsumer<? super SearchResults, ? super Throwable> callback) {
+ AndroidFuture<byte[]> future = new AndroidFuture<>();
+ future.whenCompleteAsync((searchResultBytes, err) -> {
+ if (err != null) {
+ callback.accept(null, err);
+ return;
+ }
+
+ if (searchResultBytes != null) {
+ SearchResultProto searchResultProto;
+ try {
+ searchResultProto = SearchResultProto.parseFrom(searchResultBytes);
+ } catch (InvalidProtocolBufferException e) {
+ callback.accept(null, e);
+ return;
+ }
+ if (searchResultProto.hasError()) {
+ // TODO(sidchhabra): Add better exception handling.
+ callback.accept(
+ null,
+ new RuntimeException(searchResultProto.getError().getErrorMessage()));
+ return;
+ }
+ SearchResults searchResults = new SearchResults(searchResultProto);
+ callback.accept(searchResults, null);
+ return;
+ }
+
+ // Nothing was supplied in the future at all
+ callback.accept(
+ null, new IllegalStateException("Unknown failure occurred while querying"));
+ }, executor);
+
+ try {
+ mService.query(queryExpression, searchSpec.getProto().toByteArray(), future);
+ } catch (RemoteException e) {
+ future.completeExceptionally(e);
+ }
+ }
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index fc83d8ccbd4a..22250f4cc3ec 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright 2019, 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.
@@ -29,4 +29,13 @@ interface IAppSearchManager {
*/
void setSchema(in byte[] schemaProto, in AndroidFuture callback);
void put(in byte[] documentBytes, in AndroidFuture callback);
+ /**
+ * Searches a document based on a given query string.
+ *
+ * @param queryExpression Query String to search.
+ * @param searchSpec Serialized SearchSpecProto.
+ * @param callback {@link AndroidFuture}. Will be completed with a serialized
+ * {@link SearchResultsProto}, or completed exceptionally if query fails.
+ */
+ void query(in String queryExpression, in byte[] searchSpecBytes, in AndroidFuture callback);
}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
new file mode 100644
index 000000000000..0d029f029ee5
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/IllegalSearchSpecException.java
@@ -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.
+ */
+
+package android.app.appsearch;
+
+import android.annotation.NonNull;
+
+/**
+ * Indicates that a {@link android.app.appsearch.SearchResults} has logical inconsistencies such
+ * as unpopulated mandatory fields or illegal combinations of parameters.
+ *
+ * @hide
+ */
+public class IllegalSearchSpecException extends IllegalArgumentException {
+ /**
+ * Constructs a new {@link IllegalSearchSpecException}.
+ *
+ * @param message A developer-readable description of the issue with the bundle.
+ */
+ public IllegalSearchSpecException(@NonNull String message) {
+ super(message);
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
new file mode 100644
index 000000000000..ec4258d08655
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.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 android.app.appsearch;
+
+import android.annotation.NonNull;
+
+import com.google.android.icing.proto.SearchResultProto;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * SearchResults are a list of results that are returned from a query. Each result from this
+ * list contains a document and may contain other fields like snippets based on request.
+ * @hide
+ */
+public final class SearchResults {
+
+ private final SearchResultProto mSearchResultProto;
+
+ /** @hide */
+ public SearchResults(SearchResultProto searchResultProto) {
+ mSearchResultProto = searchResultProto;
+ }
+
+ /**
+ * This class represents the result obtained from the query. It will contain the document which
+ * which matched the specified query string and specifications.
+ * @hide
+ */
+ public static final class Result {
+ private final SearchResultProto.ResultProto mResultProto;
+
+ private Result(SearchResultProto.ResultProto resultProto) {
+ mResultProto = resultProto;
+ }
+
+ /**
+ * Contains the matching {@link AppSearch.Document}.
+ * @return Document object which matched the query.
+ * @hide
+ */
+ // TODO(sidchhabra): Switch to Document constructor that takes proto.
+ @NonNull
+ public AppSearch.Document getDocument() {
+ return AppSearch.Document.newBuilder(mResultProto.getDocument().getUri(),
+ mResultProto.getDocument().getSchema())
+ .setCreationTimestampSecs(mResultProto.getDocument().getCreationTimestampSecs())
+ .setScore(mResultProto.getDocument().getScore())
+ .build();
+ }
+
+ // TODO(sidchhabra): Add Getter for ResultReader for Snippet.
+ }
+
+ @Override
+ public String toString() {
+ return mSearchResultProto.toString();
+ }
+
+ /**
+ * Returns a {@link Result} iterator. Returns Empty Iterator if there are no matching results.
+ * @hide
+ */
+ @NonNull
+ public Iterator<Result> getResults() {
+ List<Result> results = new ArrayList<>();
+ // TODO(sidchhabra): Pass results using a RemoteStream.
+ for (SearchResultProto.ResultProto resultProto : mSearchResultProto.getResultsList()) {
+ results.add(new Result(resultProto));
+ }
+ return results.iterator();
+ }
+}
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
new file mode 100644
index 000000000000..5df7108fec09
--- /dev/null
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java
@@ -0,0 +1,127 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+
+import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.proto.TermMatchType;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class represents the specification logic for AppSearch. It can be used to set the type of
+ * search, like prefix or exact only or apply filters to search for a specific schema type only etc.
+ * @hide
+ *
+ */
+// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
+public final class SearchSpec {
+
+ private final SearchSpecProto mSearchSpecProto;
+
+ private SearchSpec(SearchSpecProto searchSpecProto) {
+ mSearchSpecProto = searchSpecProto;
+ }
+
+ /** Creates a new {@link SearchSpec.Builder}. */
+ @NonNull
+ public static SearchSpec.Builder newBuilder() {
+ return new SearchSpec.Builder();
+ }
+
+ /** @hide */
+ @NonNull
+ SearchSpecProto getProto() {
+ return mSearchSpecProto;
+ }
+
+ /** Term Match Type for the query. */
+ // NOTE: The integer values of these constants must match the proto enum constants in
+ // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType}
+ @IntDef(prefix = {"TERM_MATCH_TYPE_"}, value = {
+ TERM_MATCH_TYPE_EXACT_ONLY,
+ TERM_MATCH_TYPE_PREFIX
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TermMatchTypeCode {}
+
+ public static final int TERM_MATCH_TYPE_EXACT_ONLY = 1;
+ public static final int TERM_MATCH_TYPE_PREFIX = 2;
+
+ /** Builder for {@link SearchSpec objects}. */
+ public static final class Builder {
+
+ private final SearchSpecProto.Builder mBuilder = SearchSpecProto.newBuilder();
+
+ private Builder(){}
+
+ /**
+ * Indicates how the query terms should match {@link TermMatchTypeCode} in the index.
+ *
+ * TermMatchType.Code=EXACT_ONLY
+ * Query terms will only match exact tokens in the index.
+ * Ex. A query term "foo" will only match indexed token "foo", and not "foot"
+ * or "football"
+ *
+ * TermMatchType.Code=PREFIX
+ * Query terms will match indexed tokens when the query term is a prefix of
+ * the token.
+ * Ex. A query term "foo" will match indexed tokens like "foo", "foot", and
+ * "football".
+ */
+ @NonNull
+ public Builder setTermMatchType(@TermMatchTypeCode int termMatchTypeCode) {
+ TermMatchType.Code termMatchTypeCodeProto =
+ TermMatchType.Code.forNumber(termMatchTypeCode);
+ if (termMatchTypeCodeProto == null) {
+ throw new IllegalArgumentException("Invalid term match type: " + termMatchTypeCode);
+ }
+ mBuilder.setTermMatchType(termMatchTypeCodeProto);
+ return this;
+ }
+
+ /**
+ * Adds a Schema type filter to {@link SearchSpec} Entry.
+ * Only search for documents that have the specified schema types.
+ * If unset, the query will search over all schema types.
+ */
+ @NonNull
+ public Builder setSchemaTypes(@NonNull String... schemaTypes) {
+ for (String schemaType : schemaTypes) {
+ mBuilder.addSchemaTypeFilters(schemaType);
+ }
+ return this;
+ }
+
+ /**
+ * Constructs a new {@link SearchSpec} from the contents of this builder.
+ *
+ * <p>After calling this method, the builder must no longer be used.
+ */
+ @NonNull
+ public SearchSpec build() {
+ if (mBuilder.getTermMatchType() == TermMatchType.Code.UNKNOWN) {
+ throw new IllegalSearchSpecException("Missing termMatchType field.");
+ }
+ return new SearchSpec(mBuilder.build());
+ }
+ }
+
+}
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 ce7e04c8ce04..f63abd945bdd 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.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.
@@ -15,6 +15,7 @@
*/
package com.android.server.appsearch;
+import android.annotation.NonNull;
import android.app.appsearch.IAppSearchManager;
import android.content.Context;
import android.os.Binder;
@@ -24,9 +25,13 @@ import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import com.android.server.appsearch.impl.AppSearchImpl;
+import com.android.server.appsearch.impl.FakeIcing;
import com.android.server.appsearch.impl.ImplInstanceManager;
import com.google.android.icing.proto.SchemaProto;
+import com.google.android.icing.proto.SearchResultProto;
+import com.google.android.icing.proto.SearchSpecProto;
+import com.google.android.icing.protobuf.InvalidProtocolBufferException;
/**
* TODO(b/142567528): add comments when implement this class
@@ -35,8 +40,11 @@ public class AppSearchManagerService extends SystemService {
public AppSearchManagerService(Context context) {
super(context);
+ mFakeIcing = new FakeIcing();
}
+ private final FakeIcing mFakeIcing;
+
@Override
public void onStart() {
publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
@@ -70,5 +78,22 @@ public class AppSearchManagerService extends SystemService {
callback.completeExceptionally(t);
}
}
+ // TODO(sidchhabra):Init FakeIcing properly.
+ // TODO(sidchhabra): Do this in a threadpool.
+ @Override
+ public void query(@NonNull String queryExpression, @NonNull byte[] searchSpec,
+ AndroidFuture callback) {
+ Preconditions.checkNotNull(queryExpression);
+ Preconditions.checkNotNull(searchSpec);
+ SearchSpecProto searchSpecProto = null;
+ try {
+ searchSpecProto = SearchSpecProto.parseFrom(searchSpec);
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException(e);
+ }
+ SearchResultProto searchResults =
+ mFakeIcing.query(queryExpression);
+ callback.complete(searchResults.toByteArray());
+ }
}
}
diff --git a/api/current.txt b/api/current.txt
index e27c318c09b9..119f0478fe4b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6831,6 +6831,7 @@ package android.app.admin {
method public boolean isApplicationHidden(@NonNull android.content.ComponentName, String);
method public boolean isBackupServiceEnabled(@NonNull android.content.ComponentName);
method @Deprecated public boolean isCallerApplicationRestrictionsManagingPackage();
+ method public boolean isCommonCriteriaModeEnabled(@NonNull android.content.ComponentName);
method public boolean isDeviceIdAttestationSupported();
method public boolean isDeviceOwnerApp(String);
method public boolean isEphemeralUser(@NonNull android.content.ComponentName);
@@ -6879,6 +6880,7 @@ package android.app.admin {
method public void setBluetoothContactSharingDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCameraDisabled(@NonNull android.content.ComponentName, boolean);
method @Deprecated public void setCertInstallerPackage(@NonNull android.content.ComponentName, @Nullable String) throws java.lang.SecurityException;
+ method public void setCommonCriteriaModeEnabled(@NonNull android.content.ComponentName, boolean);
method public void setCrossProfileCalendarPackages(@NonNull android.content.ComponentName, @Nullable java.util.Set<java.lang.String>);
method public void setCrossProfileCallerIdDisabled(@NonNull android.content.ComponentName, boolean);
method public void setCrossProfileContactsSearchDisabled(@NonNull android.content.ComponentName, boolean);
@@ -24260,6 +24262,9 @@ package android.media {
method public int write(@NonNull float[], int, int, int);
method public int write(@NonNull java.nio.ByteBuffer, int, int);
method public int write(@NonNull java.nio.ByteBuffer, int, int, long);
+ field public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1; // 0x1
+ field public static final int ENCAPSULATION_MODE_HANDLE = 2; // 0x2
+ field public static final int ENCAPSULATION_MODE_NONE = 0; // 0x0
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
@@ -24286,10 +24291,12 @@ package android.media {
method @NonNull public android.media.AudioTrack.Builder setAudioAttributes(@NonNull android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
method @NonNull public android.media.AudioTrack.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException;
method @NonNull public android.media.AudioTrack.Builder setBufferSizeInBytes(@IntRange(from=0) int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.AudioTrack.Builder setEncapsulationMode(int);
method @NonNull public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
method @NonNull public android.media.AudioTrack.Builder setPerformanceMode(int);
method @NonNull public android.media.AudioTrack.Builder setSessionId(@IntRange(from=1) int) throws java.lang.IllegalArgumentException;
method @NonNull public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
+ method @NonNull public android.media.AudioTrack.Builder setTunerConfiguration(@NonNull android.media.AudioTrack.TunerConfiguration);
}
public static final class AudioTrack.MetricsConstants {
@@ -24317,6 +24324,18 @@ package android.media {
method public void onTearDown(@NonNull android.media.AudioTrack);
}
+ public static class AudioTrack.TunerConfiguration {
+ method public int getContentId();
+ method public int getSyncId();
+ }
+
+ public static class AudioTrack.TunerConfiguration.Builder {
+ ctor public AudioTrack.TunerConfiguration.Builder();
+ method @NonNull public android.media.AudioTrack.TunerConfiguration build();
+ method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int);
+ method @NonNull public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int);
+ }
+
public class CamcorderProfile {
method public static android.media.CamcorderProfile get(int);
method public static android.media.CamcorderProfile get(int, int);
@@ -36226,6 +36245,7 @@ package android.os {
method public boolean isUserUnlocked();
method public boolean isUserUnlocked(android.os.UserHandle);
method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle);
+ method public boolean requestQuietModeEnabled(boolean, @NonNull android.os.UserHandle, int);
method @Deprecated public boolean setRestrictionsChallenge(String);
method @Deprecated public void setUserRestriction(String, boolean);
method @Deprecated public void setUserRestrictions(android.os.Bundle);
@@ -36287,6 +36307,7 @@ package android.os {
field public static final String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
+ field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
field public static final int USER_OPERATION_ERROR_CURRENT_USER = 4; // 0x4
@@ -39750,8 +39771,8 @@ package android.provider {
field public static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
field public static final String EXTRA_NOTIFICATION_LISTENER_COMPONENT_NAME = "android.provider.extra.NOTIFICATION_LISTENER_COMPONENT_NAME";
field public static final String EXTRA_SUB_ID = "android.provider.extra.SUB_ID";
- field public static final String EXTRA_WIFI_CONFIGURATION_LIST = "android.provider.extra.WIFI_CONFIGURATION_LIST";
- field public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST = "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+ field public static final String EXTRA_WIFI_NETWORK_LIST = "android.provider.extra.WIFI_NETWORK_LIST";
+ field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG";
field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON";
}
@@ -55697,7 +55718,7 @@ package android.view.textclassifier {
method public int describeContents();
method @NonNull public android.os.Bundle getExtras();
method @NonNull public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
- method @NonNull public String getText();
+ method @NonNull public CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
field public static final int APPLY_STRATEGY_REPLACE = 1; // 0x1
diff --git a/api/module-app-current.txt b/api/module-app-current.txt
index 4307e675e431..db774ef8ea2e 100644
--- a/api/module-app-current.txt
+++ b/api/module-app-current.txt
@@ -7,3 +7,11 @@ package android.app {
}
+package android.provider {
+
+ public static final class Settings.Global extends android.provider.Settings.NameValueTable {
+ field public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
+ }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index e532a3afab8a..0167bec53dc7 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -661,6 +661,7 @@ package android.app {
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+ method public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback);
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long);
method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException;
@@ -668,6 +669,7 @@ package android.app {
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent);
method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
+ method public void unregisterPullAtomCallback(int);
field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS";
field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
@@ -676,6 +678,23 @@ package android.app {
field public static final String EXTRA_STATS_DIMENSIONS_VALUE = "android.app.extra.STATS_DIMENSIONS_VALUE";
field public static final String EXTRA_STATS_SUBSCRIPTION_ID = "android.app.extra.STATS_SUBSCRIPTION_ID";
field public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
+ field public static final int PULL_SKIP = 1; // 0x1
+ field public static final int PULL_SUCCESS = 0; // 0x0
+ }
+
+ public static class StatsManager.PullAtomMetadata {
+ }
+
+ public static class StatsManager.PullAtomMetadata.Builder {
+ ctor public StatsManager.PullAtomMetadata.Builder();
+ method @NonNull public android.app.StatsManager.PullAtomMetadata build();
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setAdditiveFields(@NonNull int[]);
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setCoolDownNs(long);
+ method @NonNull public android.app.StatsManager.PullAtomMetadata.Builder setTimeoutNs(long);
+ }
+
+ public static interface StatsManager.StatsPullAtomCallback {
+ method public int onPullAtom(int, @NonNull java.util.List<android.util.StatsEvent>);
}
public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
@@ -4660,7 +4679,10 @@ package android.media.tv.tuner {
public final class Tuner implements java.lang.AutoCloseable {
ctor public Tuner(@NonNull android.content.Context);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Tuner.Descrambler openDescrambler();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.FrontendSettings);
}
@@ -4742,6 +4764,17 @@ package android.media.tv.tuner.filter {
}
+package android.media.tv.tuner.frontend {
+
+ public interface OnTuneEventListener {
+ method public void onTuneEvent(int);
+ field public static final int SIGNAL_LOCKED = 0; // 0x0
+ field public static final int SIGNAL_LOST_LOCK = 2; // 0x2
+ field public static final int SIGNAL_NO_SIGNAL = 1; // 0x1
+ }
+
+}
+
package android.metrics {
public class LogMaker {
@@ -6205,6 +6238,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoin(int, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void allowAutojoinPasspoint(@NonNull String, boolean);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void clearWifiConnectedNetworkScorer();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
@@ -6249,6 +6283,7 @@ package android.net.wifi {
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+ 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 startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
@@ -6338,6 +6373,11 @@ package android.net.wifi {
method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
}
+ public static interface WifiManager.ScoreChangeCallback {
+ method public void onStatusChange(int, boolean);
+ method public void onTriggerUpdateOfWifiUsabilityStats(int);
+ }
+
public static interface WifiManager.SoftApCallback {
method public default void onBlockedClientConnecting(@NonNull android.net.wifi.WifiClient, int);
method public default void onCapabilityChanged(@NonNull android.net.wifi.SoftApCapability);
@@ -6354,6 +6394,12 @@ package android.net.wifi {
field public static final int DATA_ACTIVITY_OUT = 2; // 0x2
}
+ public static interface WifiManager.WifiConnectedNetworkScorer {
+ method public void setScoreChangeCallback(@NonNull android.net.wifi.WifiManager.ScoreChangeCallback);
+ method public void start(int);
+ method public void stop(int);
+ }
+
public class WifiNetworkConnectionStatistics implements android.os.Parcelable {
ctor public WifiNetworkConnectionStatistics(int, int);
ctor public WifiNetworkConnectionStatistics();
@@ -6730,6 +6776,15 @@ package android.net.wifi.rtt {
package android.net.wifi.wificond {
+ public final class DeviceWiphyCapabilities implements android.os.Parcelable {
+ ctor public DeviceWiphyCapabilities();
+ method public int describeContents();
+ method public boolean isWifiStandardSupported(int);
+ method public void setWifiStandardSupport(int, boolean);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.DeviceWiphyCapabilities> CREATOR;
+ }
+
public final class NativeScanResult implements android.os.Parcelable {
method public int describeContents();
method @NonNull public byte[] getBssid();
@@ -6794,6 +6849,7 @@ package android.net.wifi.wificond {
method public void abortScan(@NonNull String);
method public void enableVerboseLogging(boolean);
method @NonNull public int[] getChannelsMhzForBand(int);
+ method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
method public boolean initialize(@NonNull Runnable);
@@ -10681,6 +10737,7 @@ package android.telephony {
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isGlobalModeEnabled();
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 isManualNetworkSelectionAllowed();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a11f41fbc5d0..bc7e1e591021 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4342,6 +4342,9 @@ public class AppOpsManager {
mHistoricalUidOps.removeAt(i);
} else {
uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
+ if (uidOp.getPackageCount() == 0) {
+ mHistoricalUidOps.removeAt(i);
+ }
}
}
}
@@ -4681,6 +4684,9 @@ public class AppOpsManager {
mHistoricalPackageOps.removeAt(i);
} else {
packageOps.filter(featureId, opNames, filter, fractionToRemove);
+ if (packageOps.getFeatureCount() == 0) {
+ mHistoricalPackageOps.removeAt(i);
+ }
}
}
}
@@ -4930,6 +4936,9 @@ public class AppOpsManager {
mHistoricalFeatureOps.removeAt(i);
} else {
featureOps.filter(opNames, filter, fractionToRemove);
+ if (featureOps.getOpCount() == 0) {
+ mHistoricalFeatureOps.removeAt(i);
+ }
}
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7af7a4adc748..f6014e5fdf80 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -95,7 +95,7 @@ interface INotificationManager
void updateNotificationChannelForPackage(String pkg, int uid, in NotificationChannel channel);
NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId);
- void createConversationNotificationChannelForPackage(String pkg, int uid, in NotificationChannel parentChannel, String conversationId);
+ void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId);
NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
void deleteNotificationChannel(String pkg, String channelId);
void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index d23754e9d433..7ab85a4a7468 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1208,8 +1208,7 @@ public class ResourcesManager {
WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
- applyConfigurationToResourcesLocked(config, compat, tmpConfig,
- defaultDisplayMetrics, key, r);
+ applyConfigurationToResourcesLocked(config, compat, tmpConfig, key, r);
} else {
mResourceImpls.removeAt(i);
}
@@ -1224,8 +1223,7 @@ public class ResourcesManager {
}
applyConfigurationToResourcesLocked(config, compat, tmpConfig,
- defaultDisplayMetrics, resourcesWithLoaders.resourcesKey(),
- resources.getImpl());
+ resourcesWithLoaders.resourcesKey(), resources.getImpl());
}
return changes != 0;
@@ -1236,40 +1234,33 @@ public class ResourcesManager {
private void applyConfigurationToResourcesLocked(@NonNull Configuration config,
@Nullable CompatibilityInfo compat, Configuration tmpConfig,
- DisplayMetrics defaultDisplayMetrics, ResourcesKey key, ResourcesImpl resourcesImpl) {
+ ResourcesKey key, ResourcesImpl resourcesImpl) {
if (DEBUG || DEBUG_CONFIGURATION) {
Slog.v(TAG, "Changing resources "
+ resourcesImpl + " config to: " + config);
}
int displayId = key.mDisplayId;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- DisplayMetrics dm = defaultDisplayMetrics;
final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
- if (!isDefaultDisplay || hasOverrideConfiguration) {
- tmpConfig.setTo(config);
-
- // Get new DisplayMetrics based on the DisplayAdjustments given
- // to the ResourcesImpl. Update a copy if the CompatibilityInfo
- // changed, because the ResourcesImpl object will handle the
- // update internally.
- DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
- if (compat != null) {
- daj = new DisplayAdjustments(daj);
- daj.setCompatibilityInfo(compat);
- }
- dm = getDisplayMetrics(displayId, daj);
-
- if (!isDefaultDisplay) {
- applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
- }
+ tmpConfig.setTo(config);
+
+ // Get new DisplayMetrics based on the DisplayAdjustments given to the ResourcesImpl. Update
+ // a copy if the CompatibilityInfo changed, because the ResourcesImpl object will handle the
+ // update internally.
+ DisplayAdjustments daj = resourcesImpl.getDisplayAdjustments();
+ if (compat != null) {
+ daj = new DisplayAdjustments(daj);
+ daj.setCompatibilityInfo(compat);
+ }
+ daj.setConfiguration(config);
+ DisplayMetrics dm = getDisplayMetrics(displayId, daj);
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
+ }
- if (hasOverrideConfiguration) {
- tmpConfig.updateFrom(key.mOverrideConfiguration);
- }
- resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
- } else {
- resourcesImpl.updateConfiguration(config, dm, compat);
+ if (hasOverrideConfiguration) {
+ tmpConfig.updateFrom(key.mOverrideConfiguration);
}
+ resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
}
/**
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index dde6dda8e448..0beceb0a1f08 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -18,11 +18,13 @@ package android.app;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
+import android.os.Binder;
import android.os.IBinder;
import android.os.IPullAtomCallback;
import android.os.IPullAtomResultReceiver;
@@ -108,13 +110,11 @@ public final class StatsManager {
/**
* Value indicating that this pull was successful and that the result should be used.
*
- * @hide
**/
public static final int PULL_SUCCESS = 0;
/**
* Value indicating that this pull was unsuccessful and that the result should not be used.
- * @hide
**/
public static final int PULL_SKIP = 1;
@@ -512,6 +512,17 @@ public final class StatsManager {
/**
+ * Temp registration for while the migration is in progress.
+ *
+ * @hide
+ */
+ public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
+ @NonNull StatsPullAtomCallback callback,
+ @NonNull @CallbackExecutor Executor executor) {
+ registerPullAtomCallback(atomTag, metadata, executor, callback);
+ }
+
+ /**
* Registers a callback for an atom when that atom is to be pulled. The stats service will
* invoke pullData in the callback when the stats service determines that this atom needs to be
* pulled.
@@ -520,18 +531,19 @@ public final class StatsManager {
* @param metadata Optional metadata specifying the timeout, cool down time, and
* additive fields for mapping isolated to host uids.
* @param callback The callback to be invoked when the stats service pulls the atom.
- * @param executor The executor in which to run the callback
+ * @param executor The executor in which to run the callback.
*
- * @hide
*/
public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata,
- @NonNull StatsPullAtomCallback callback, @NonNull Executor executor) {
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull StatsPullAtomCallback callback) {
long coolDownNs = metadata == null ? DEFAULT_COOL_DOWN_NS : metadata.mCoolDownNs;
long timeoutNs = metadata == null ? DEFAULT_TIMEOUT_NS : metadata.mTimeoutNs;
int[] additiveFields = metadata == null ? new int[0] : metadata.mAdditiveFields;
if (additiveFields == null) {
additiveFields = new int[0];
}
+
synchronized (sLock) {
try {
IStatsManagerService service = getIStatsManagerServiceLocked();
@@ -551,7 +563,6 @@ public final class StatsManager {
*
* @param atomTag The tag of the atom of which to unregister
*
- * @hide
*/
public void unregisterPullAtomCallback(int atomTag) {
synchronized (sLock) {
@@ -577,21 +588,26 @@ public final class StatsManager {
@Override
public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
- mExecutor.execute(() -> {
- List<StatsEvent> data = new ArrayList<>();
- int successInt = mCallback.onPullAtom(atomTag, data);
- boolean success = successInt == PULL_SUCCESS;
- StatsEventParcel[] parcels = new StatsEventParcel[data.size()];
- for (int i = 0; i < data.size(); i++) {
- parcels[i] = new StatsEventParcel();
- parcels[i].buffer = data.get(i).getBytes();
- }
- try {
- resultReceiver.pullFinished(atomTag, success, parcels);
- } catch (RemoteException e) {
- Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
- }
- });
+ long token = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ List<StatsEvent> data = new ArrayList<>();
+ int successInt = mCallback.onPullAtom(atomTag, data);
+ boolean success = successInt == PULL_SUCCESS;
+ StatsEventParcel[] parcels = new StatsEventParcel[data.size()];
+ for (int i = 0; i < data.size(); i++) {
+ parcels[i] = new StatsEventParcel();
+ parcels[i].buffer = data.get(i).getBytes();
+ }
+ try {
+ resultReceiver.pullFinished(atomTag, success, parcels);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
@@ -599,7 +615,6 @@ public final class StatsManager {
* Metadata required for registering a StatsPullAtomCallback.
* All fields are optional, and defaults will be used for fields that are unspecified.
*
- * @hide
*/
public static class PullAtomMetadata {
private final long mCoolDownNs;
@@ -614,22 +629,27 @@ public final class StatsManager {
}
/**
- * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for
- * StatsManager#registerPullAtomCallback
+ * Temp for while migrations are in progress.
+ *
+ * @hide
*/
public static PullAtomMetadata.Builder newBuilder() {
return new PullAtomMetadata.Builder();
}
/**
- * Builder for PullAtomMetadata.
+ * Builder for PullAtomMetadata.
*/
public static class Builder {
private long mCoolDownNs;
private long mTimeoutNs;
private int[] mAdditiveFields;
- private Builder() {
+ /**
+ * Returns a new PullAtomMetadata.Builder object for constructing PullAtomMetadata for
+ * StatsManager#registerPullAtomCallback
+ */
+ public Builder() {
mCoolDownNs = DEFAULT_COOL_DOWN_NS;
mTimeoutNs = DEFAULT_TIMEOUT_NS;
mAdditiveFields = null;
@@ -662,7 +682,7 @@ public final class StatsManager {
* will be combined when the non-additive fields are the same.
*/
@NonNull
- public Builder setAdditiveFields(int[] additiveFields) {
+ public Builder setAdditiveFields(@NonNull int[] additiveFields) {
mAdditiveFields = additiveFields;
return this;
}
@@ -705,14 +725,13 @@ public final class StatsManager {
/**
* Callback interface for pulling atoms requested by the stats service.
*
- * @hide
*/
public interface StatsPullAtomCallback {
/**
* Pull data for the specified atom tag, filling in the provided list of StatsEvent data.
* @return {@link #PULL_SUCCESS} if the pull was successful, or {@link #PULL_SKIP} if not.
*/
- int onPullAtom(int atomTag, List<StatsEvent> data);
+ int onPullAtom(int atomTag, @NonNull List<StatsEvent> data);
}
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index be8e1d60f290..54a64ef3f392 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11493,4 +11493,48 @@ public class DevicePolicyManager {
}
return Collections.emptyList();
}
+
+ /**
+ * Called by device owner or profile owner of an organization-owned managed profile to toggle
+ * Common Criteria mode for the device. When the device is in Common Criteria mode,
+ * certain device functionalities are tuned to meet the higher
+ * security level required by Common Criteria certification. For example:
+ * <ul>
+ * <li> Bluetooth long term key material is additionally integrity-protected with AES-GCM. </li>
+ * <li> WiFi configuration store is additionally integrity-protected with AES-GCM. </li>
+ * </ul>
+ * Common Criteria mode is disabled by default.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @param enabled whether Common Criteria mode should be enabled or not.
+ */
+ public void setCommonCriteriaModeEnabled(@NonNull ComponentName admin, boolean enabled) {
+ throwIfParentInstance("setCommonCriteriaModeEnabled");
+ if (mService != null) {
+ try {
+ mService.setCommonCriteriaModeEnabled(admin, enabled);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Called by device owner or profile owner of an organization-owned managed profile to return
+ * whether Common Criteria mode is currently enabled for the device.
+ *
+ * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+ * @return {@code true} if Common Criteria mode is enabled, {@code false} otherwise.
+ */
+ public boolean isCommonCriteriaModeEnabled(@NonNull ComponentName admin) {
+ throwIfParentInstance("isCommonCriteriaModeEnabled");
+ if (mService != null) {
+ try {
+ return mService.isCommonCriteriaModeEnabled(admin);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 21c9eb5c60ad..f649286206bb 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -461,4 +461,7 @@ interface IDevicePolicyManager {
void setProtectedPackages(in ComponentName admin, in List<String> packages);
List<String> getProtectedPackages(in ComponentName admin);
+
+ void setCommonCriteriaModeEnabled(in ComponentName admin, boolean enabled);
+ boolean isCommonCriteriaModeEnabled(in ComponentName admin);
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 6df92a78cc9f..a34be5c3edc7 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -75,9 +75,15 @@ public class TransactionExecutorHelper {
mLifecycleSequence.clear();
if (finish >= start) {
- // just go there
- for (int i = start + 1; i <= finish; i++) {
- mLifecycleSequence.add(i);
+ if (start == ON_START && finish == ON_STOP) {
+ // A case when we from start to stop state soon, we don't need to go
+ // through the resumed, paused state.
+ mLifecycleSequence.add(ON_STOP);
+ } else {
+ // just go there
+ for (int i = start + 1; i <= finish; i++) {
+ mLifecycleSequence.add(i);
+ }
}
} else { // finish < start, can't just cycle down
if (start == ON_PAUSE && finish == ON_RESUME) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 679de8a281c3..3df94a7e233a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -30,6 +30,7 @@ import android.annotation.StringDef;
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.StyleableRes;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -3930,6 +3931,7 @@ public abstract class Context {
*/
@SystemApi
@TestApi
+ @SuppressLint("ServiceName")
public static final String STATUS_BAR_SERVICE = "statusbar";
/**
@@ -4024,6 +4026,7 @@ public abstract class Context {
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
@SystemApi
+ @SuppressLint("ServiceName")
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
/** {@hide} */
public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
@@ -4048,6 +4051,7 @@ public abstract class Context {
* @hide
*/
@SystemApi
+ @SuppressLint("ServiceName")
public static final String WIFI_COND_SERVICE = "wificond";
/**
@@ -4379,6 +4383,7 @@ public abstract class Context {
* @see #getSystemService(String)
*/
@TestApi
+ @SuppressLint("ServiceName") // TODO: This should be renamed to CONTENT_CAPTURE_SERVICE
public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture";
/**
@@ -4474,6 +4479,7 @@ public abstract class Context {
* @hide
*/
@TestApi
+ @SuppressLint("ServiceName") // TODO: This should be renamed to DEVICE_IDLE_SERVICE
public static final String DEVICE_IDLE_CONTROLLER = "deviceidle";
/**
@@ -4483,6 +4489,7 @@ public abstract class Context {
* @hide
*/
@TestApi
+ @SuppressLint("ServiceName") // TODO: This should be renamed to POWER_WHITELIST_SERVICE
public static final String POWER_WHITELIST_MANAGER = "power_whitelist";
/**
@@ -5053,6 +5060,7 @@ public abstract class Context {
* @hide
*/
@SystemApi
+ @SuppressLint("ServiceName")
public static final String BATTERY_STATS_SERVICE = "batterystats";
/**
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 799dff9632c8..fb5f136f2fca 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -26,6 +26,7 @@ import android.annotation.TestApi;
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Point;
import android.media.projection.MediaProjection;
import android.os.Handler;
@@ -400,10 +401,10 @@ public final class DisplayManager {
if (display == null) {
// TODO: We cannot currently provide any override configurations for metrics on displays
// other than the display the context is associated with.
- final Context context = mContext.getDisplayId() == displayId
- ? mContext : mContext.getApplicationContext();
+ final Resources resources = mContext.getDisplayId() == displayId
+ ? mContext.getResources() : null;
- display = mGlobal.getCompatibleDisplay(displayId, context.getResources());
+ display = mGlobal.getCompatibleDisplay(displayId, resources);
if (display != null) {
mDisplays.put(displayId, display);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index edaaf81cd906..33d613152bc1 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -113,7 +113,7 @@ interface IUserManager {
boolean isUserRunning(int userId);
boolean isUserNameSet(int userId);
boolean hasRestrictedProfiles();
- boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target);
+ boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
String getUserName();
long getUserStartRealtime();
long getUserUnlockRealtime();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 82b04a661b54..0414b14ae02d 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -373,9 +373,15 @@ public final class PowerManager {
public static final int GO_TO_SLEEP_REASON_INATTENTIVE = 9;
/**
+ * Go to sleep reason code: Going to sleep due to quiescent boot.
* @hide
*/
- public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_INATTENTIVE;
+ public static final int GO_TO_SLEEP_REASON_QUIESCENT = 10;
+
+ /**
+ * @hide
+ */
+ public static final int GO_TO_SLEEP_REASON_MAX = GO_TO_SLEEP_REASON_QUIESCENT;
/**
* @hide
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2eaefca0efa3..12e843c87481 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -133,6 +133,22 @@ public class UserManager {
public static final String USER_TYPE_SYSTEM_HEADLESS = "android.os.usertype.system.HEADLESS";
/**
+ * Flag passed to {@link #requestQuietModeEnabled} to request disabling quiet mode only if
+ * there is no need to confirm the user credentials. If credentials are required to disable
+ * quiet mode, {@link #requestQuietModeEnabled} will do nothing and return {@code false}.
+ */
+ public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 0x1;
+
+ /**
+ * List of flags available for the {@link #requestQuietModeEnabled} method.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "QUIET_MODE_" }, value = {
+ QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED })
+ public @interface QuietModeFlag {}
+
+ /**
* @hide
* No user restriction.
*/
@@ -3216,6 +3232,25 @@ public class UserManager {
}
/**
+ * Perform the same operation as {@link #requestQuietModeEnabled(boolean, UserHandle)}, but
+ * with a flag to tweak the behavior of the request.
+ *
+ * @param enableQuietMode whether quiet mode should be enabled or disabled
+ * @param userHandle user handle of the profile
+ * @param flags Can be 0 or {@link #QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED}.
+ * @return {@code false} if user's credential is needed in order to turn off quiet mode,
+ * {@code true} otherwise
+ * @throws SecurityException if the caller is invalid
+ * @throws IllegalArgumentException if {@code userHandle} is not a managed profile
+ *
+ * @see #isQuietModeEnabled(UserHandle)
+ */
+ public boolean requestQuietModeEnabled(boolean enableQuietMode, @NonNull UserHandle userHandle,
+ @QuietModeFlag int flags) {
+ return requestQuietModeEnabled(enableQuietMode, userHandle, null, flags);
+ }
+
+ /**
* Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
* a target to start when user is unlocked. If {@code target} is specified, caller must have
* the {@link android.Manifest.permission#MANAGE_USERS} permission.
@@ -3225,9 +3260,23 @@ public class UserManager {
*/
public boolean requestQuietModeEnabled(
boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target) {
+ return requestQuietModeEnabled(enableQuietMode, userHandle, target, 0);
+ }
+ /**
+ * Similar to {@link #requestQuietModeEnabled(boolean, UserHandle)}, except you can specify
+ * a target to start when user is unlocked. If {@code target} is specified, caller must have
+ * the {@link android.Manifest.permission#MANAGE_USERS} permission.
+ *
+ * @see {@link #requestQuietModeEnabled(boolean, UserHandle)}
+ * @hide
+ */
+ public boolean requestQuietModeEnabled(
+ boolean enableQuietMode, @NonNull UserHandle userHandle, IntentSender target,
+ int flags) {
try {
return mService.requestQuietModeEnabled(
- mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target);
+ mContext.getPackageName(), enableQuietMode, userHandle.getIdentifier(), target,
+ flags);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f0a11748fbd6..3ea64f13fe6b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -876,14 +876,7 @@ public class StorageManager {
*/
public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
Preconditions.checkNotNull(path);
- String pathString = path.getCanonicalPath();
- if (path.getPath().startsWith("/sdcard")) {
- // On FUSE enabled devices, realpath(2) /sdcard is /mnt/user/<userid>/emulated/<userid>
- // as opposed to /storage/emulated/<userid>.
- // And vol.path below expects to match with a path starting with /storage
- pathString = pathString.replaceFirst("^/mnt/user/[0-9]+/", "/storage/");
- }
-
+ final String pathString = path.getCanonicalPath();
if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
return UUID_DEFAULT;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f6633201ec73..0e3dd3a8292a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13929,6 +13929,19 @@ public final class Settings {
*/
public static final String POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE =
"power_button_suppression_delay_after_gesture_wake";
+
+ /**
+ * An integer indicating whether the device is in Common Criteria mode. When enabled,
+ * certain device functionalities are tuned to meet the higher security level required
+ * by Common Criteria certification. Examples include:
+ * Bluetooth long term key material is additionally integrity-protected with AES-GCM.
+ * WiFi configuration store is additionally integrity-protected with AES-GCM.
+ * A value of 0 means Common Criteria mode is not enabled (default), a value of non-zero
+ * means Common Criteria mode is enabled.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_APPS)
+ public static final String COMMON_CRITERIA_MODE = "common_criteria_mode";
}
/**
@@ -14360,46 +14373,74 @@ public final class Settings {
/**
* Activity Action: Show setting page to process the addition of Wi-Fi networks to the user's
- * saved network list. The app should send a new intent with an extra that holds a maximum of
- * five {@link android.net.wifi.WifiConfiguration} that specify credentials for the networks to
- * be added to the user's database. The Intent should be sent via the {@link
- * android.app.Activity#startActivityForResult(Intent, int)} API.
+ * saved network list. The app should send a new intent with an extra that holds a maximum
+ * of five {@link android.net.wifi.WifiNetworkSuggestion} that specify credentials for the
+ * networks to be added to the user's database. The Intent should be sent via the
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} API.
* <p>
* Note: The app sending the Intent to add the credentials doesn't get any ownership over the
* newly added network(s). For the Wi-Fi stack, these networks will look like the user
* manually added them from the Settings UI.
* <p>
- * Input: The app should put parcelable array list to
- * {@link android.net.wifi.WifiConfiguration} into the
- * {@link #EXTRA_WIFI_CONFIGURATION_LIST} extra.
+ * Input: The app should put parcelable array list of
+ * {@link android.net.wifi.WifiNetworkSuggestion} into the {@link #EXTRA_WIFI_NETWORK_LIST}
+ * extra.
* <p>
* Output: After {@link android.app.Activity#startActivityForResult(Intent, int)}, the
* callback {@link android.app.Activity#onActivityResult(int, int, Intent)} will have a
* result code {@link android.app.Activity#RESULT_OK} to indicate user pressed the save
* button to save the networks or {@link android.app.Activity#RESULT_CANCELED} to indicate
* that the user rejected the request. Additionally, an integer array list, stored in
- * {@link #EXTRA_WIFI_CONFIGURATION_RESULT_LIST}, will indicate the process result of
- * each network.
+ * {@link #EXTRA_WIFI_NETWORK_RESULT_LIST}, will indicate the process result of each network.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_WIFI_ADD_NETWORKS =
"android.settings.WIFI_ADD_NETWORKS";
/**
- * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates all the
- * {@link android.net.wifi.WifiConfiguration} that would be saved.
- */
- public static final String EXTRA_WIFI_CONFIGURATION_LIST =
- "android.provider.extra.WIFI_CONFIGURATION_LIST";
+ * A bundle extra of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that indicates the list
+ * of the {@link android.net.wifi.WifiNetworkSuggestion} elements. The maximum count of the
+ * {@link android.net.wifi.WifiNetworkSuggestion} elements in the list will be five.
+ * <p>
+ * For example:
+ * To provide credentials for one open and one WPA2 networks:
+ *
+ * <pre>{@code
+ * final WifiNetworkSuggestion suggestion1 =
+ * new WifiNetworkSuggestion.Builder()
+ * .setSsid("test111111")
+ * .build();
+ * final WifiNetworkSuggestion suggestion2 =
+ * new WifiNetworkSuggestion.Builder()
+ * .setSsid("test222222")
+ * .setWpa2Passphrase("test123456")
+ * .build();
+ * final List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>;
+ * suggestionsList.add(suggestion1);
+ * suggestionsList.add(suggestion2);
+ * Bundle bundle = new Bundle();
+ * bundle.putParcelableArrayList(Settings.EXTRA_WIFI_NETWORK_LIST,(ArrayList<? extends
+ * Parcelable>) suggestionsList);
+ * final Intent intent = new Intent(Settings.ACTION_WIFI_ADD_NETWORKS);
+ * intent.putExtras(bundle);
+ * startActivityForResult(intent, 0);
+ * }</pre>
+ */
+ public static final String EXTRA_WIFI_NETWORK_LIST =
+ "android.provider.extra.WIFI_NETWORK_LIST";
/**
* A bundle extra of the result of {@link #ACTION_WIFI_ADD_NETWORKS} intent action that
- * indicates the action result of the saved {@link android.net.wifi.WifiConfiguration}. It's
- * value of AddWifiResult interface, and will be 1:1 mapping to the element in {@link
- * #EXTRA_WIFI_CONFIGURATION_LIST}.
+ * indicates the action result of the saved {@link android.net.wifi.WifiNetworkSuggestion}.
+ * Its value is a list of integers, and all the elements will be 1:1 mapping to the elements
+ * in {@link #EXTRA_WIFI_NETWORK_LIST}, if user press cancel to cancel the add networks
+ * request, then its value will be null.
+ * <p>
+ * Note: The integer value will be one of the {@link #ADD_WIFI_RESULT_SUCCESS},
+ * {@link #ADD_WIFI_RESULT_ADD_OR_UPDATE_FAILED}, or {@link #ADD_WIFI_RESULT_ALREADY_EXISTS}}.
*/
- public static final String EXTRA_WIFI_CONFIGURATION_RESULT_LIST =
- "android.provider.extra.WIFI_CONFIGURATION_RESULT_LIST";
+ public static final String EXTRA_WIFI_NETWORK_RESULT_LIST =
+ "android.provider.extra.WIFI_NETWORK_RESULT_LIST";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 389040cea864..4f400a8e9cb2 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -16,6 +16,9 @@
package android.service.notification;
+import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
+import static android.util.FeatureFlagUtils.*;
+
import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
@@ -29,6 +32,8 @@ import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -436,6 +441,19 @@ public class StatusBarNotification implements Parcelable {
return logMaker;
}
+ /**
+ * @hide
+ */
+ public String getShortcutId(Context context) {
+ String conversationId = getNotification().getShortcutId();
+ if (isEnabled(context, NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
+ && getNotification().getNotificationStyle() == Notification.MessagingStyle.class
+ && TextUtils.isEmpty(conversationId)) {
+ conversationId = getId() + getTag() + PLACEHOLDER_CONVERSATION_ID;
+ }
+ return conversationId;
+ }
+
private String getGroupLogTag() {
return shortenTag(getGroup());
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 178b3c0cb94e..904c510a5b01 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -454,7 +454,7 @@ public final class Display {
mResources = res;
mDisplayAdjustments = mResources != null
? new DisplayAdjustments(mResources.getConfiguration())
- : daj != null ? new DisplayAdjustments(daj) : null;
+ : daj != null ? new DisplayAdjustments(daj) : new DisplayAdjustments();
mIsValid = true;
// Cache properties that cannot change as long as the display is valid.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index cc4278bdd2b6..38416eeede32 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -159,7 +159,6 @@ public final class SurfaceControl implements Parcelable {
private static native DisplayedContentSample nativeGetDisplayedContentSample(
IBinder displayToken, long numFrames, long timestamp);
private static native int nativeGetActiveConfig(IBinder displayToken);
- private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs);
private static native SurfaceControl.DesiredDisplayConfigSpecs
@@ -1482,16 +1481,6 @@ public final class SurfaceControl implements Parcelable {
/**
- * @hide
- */
- public static boolean setActiveConfig(IBinder displayToken, int id) {
- if (displayToken == null) {
- throw new IllegalArgumentException("displayToken must not be null");
- }
- return nativeSetActiveConfig(displayToken, id);
- }
-
- /**
* Contains information about desired display configuration.
*
* @hide
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 3dfeffbf9e6a..9cbba87e6856 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1444,6 +1444,29 @@ public final class AccessibilityManager {
return null;
}
+ /**
+ *
+ * Sets an {@link IWindowMagnificationConnection} that manipulates window magnification.
+ *
+ * @param connection The connection that manipulates window magnification.
+ * @hide
+ */
+ public void setWindowMagnificationConnection(@Nullable
+ IWindowMagnificationConnection connection) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.setWindowMagnificationConnection(connection);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error setting window magnfication connection", re);
+ }
+ }
+
private IAccessibilityManager getServiceLocked() {
if (mService == null) {
tryConnectToServiceLocked(null);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index fcaaa2e74778..7f8fdf83995a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -25,6 +25,7 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
import android.view.IWindow;
/**
@@ -86,4 +87,5 @@ interface IAccessibilityManager {
oneway void registerSystemAction(in RemoteAction action, int actionId);
oneway void unregisterSystemAction(int actionId);
+ oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
new file mode 100644
index 000000000000..0b45c6bed9bd
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -0,0 +1,75 @@
+/*
+ * 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 android.view.accessibility;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * Interface for interaction between {@link AccessibilityManagerService}
+ * and {@link WindowMagnification} in SystemUI.
+ *
+ * @hide
+ */
+oneway interface IWindowMagnificationConnection {
+
+ /**
+ * Enables window magnification on specifed display with specified center and scale.
+ *
+ * @param displayId The logical display id.
+ * @param scale magnification scale.
+ * @param centerX the screen-relative X coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ * @param centerY the screen-relative Y coordinate around which to center,
+ * or {@link Float#NaN} to leave unchanged.
+ */
+ void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
+
+ /**
+ * Sets the scale of the window magnifier on specifed display.
+ *
+ * @param displayId The logical display id.
+ * @param scale magnification scale.
+ */
+ void setScale(int displayId, float scale);
+
+ /**
+ * Disables window magnification on specifed display.
+ *
+ * @param displayId The logical display id.
+ */
+ void disableWindowMagnification(int displayId);
+
+ /**
+ * Moves the window magnifier on the specifed display.
+ *
+ * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+ * current screen pixels.
+ * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+ * current screen pixels.
+ */
+ void moveWindowMagnifier(int displayId, float offsetX, float offsetY);
+
+ /**
+ * Sets {@link IWindowMagnificationConnectionCallback} to receive the request or the callback.
+ *
+ *
+ * @param callback the interface to be called.
+ */
+ void setConnectionCallback(in IWindowMagnificationConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
new file mode 100644
index 000000000000..7327bb571d59
--- /dev/null
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnectionCallback.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.view.accessibility;
+
+import android.graphics.Rect;
+
+/**
+ * interface to notify the change of the window magnifier bounds and request to change
+ * the magnification mode.
+ *
+ * @hide
+ */
+ oneway interface IWindowMagnificationConnectionCallback {
+
+ /**
+ * Called when the bounds of the window magnifier is changed.
+ *
+ * @param displayId The logical display id.
+ * @param bounds The window magnifier bounds in screen coordinates.
+ */
+ void onWindowMagnifierBoundsChanged(int display, in Rect bounds);
+ /**
+ * Changes the magnification mode on specified display. It is invoked by System UI when the
+ * switch button is toggled.
+ *
+ * @param displayId The logical display id.
+ * @param magnificationMode new magnification mode.
+ */
+ void onChangeMagnificationMode(int display, int magnificationMode);
+}
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1aa2aeccc0db..bda12b0893d1 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -113,7 +113,7 @@ public final class TextLinks implements Parcelable {
* Returns the text that was used to generate these links.
*/
@NonNull
- public String getText() {
+ public CharSequence getText() {
return mFullText;
}
@@ -370,8 +370,8 @@ public final class TextLinks implements Parcelable {
}
/**
- * @return ordered list of locale preferences that can be used to disambiguate
- * the provided text
+ * Returns an ordered list of locale preferences that can be used to disambiguate the
+ * provided text.
*/
@Nullable
public LocaleList getDefaultLocales() {
@@ -379,7 +379,8 @@ public final class TextLinks implements Parcelable {
}
/**
- * @return The config representing the set of entities to look for
+ * Returns the config representing the set of entities to look for
+ *
* @see Builder#setEntityConfig(EntityConfig)
*/
@Nullable
@@ -398,8 +399,8 @@ public final class TextLinks implements Parcelable {
}
/**
- * @return reference time based on which relative dates (e.g. "tomorrow") should be
- * interpreted.
+ * Returns reference time based on which relative dates (e.g. "tomorrow") should be
+ * interpreted.
*/
@Nullable
public ZonedDateTime getReferenceTime() {
@@ -473,6 +474,9 @@ public final class TextLinks implements Parcelable {
}
/**
+ * Sets ordered list of locale preferences that may be used to disambiguate the
+ * provided text.
+ *
* @param defaultLocales ordered list of locale preferences that may be used to
* disambiguate the provided text. If no locale preferences exist,
* set this to null or an empty locale list.
@@ -524,9 +528,11 @@ public final class TextLinks implements Parcelable {
}
/**
- * @param referenceTime reference time based on which relative dates (e.g. "tomorrow"
- * should be interpreted. This should usually be the time when the text was
- * originally composed.
+ * Sets the reference time based on which relative dates (e.g.
+ * "tomorrow") should be interpreted.
+ *
+ * @param referenceTime reference time based on which relative dates. This should
+ * usually be the time when the text was originally composed.
*
* @return this builder
*/
@@ -716,6 +722,8 @@ public final class TextLinks implements Parcelable {
}
/**
+ * Adds a TextLink.
+ *
* @see #addLink(int, int, Map)
* @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
*/
diff --git a/core/java/android/view/textclassifier/TextLinksParams.java b/core/java/android/view/textclassifier/TextLinksParams.java
index b7d63bfabd6d..f12b0d7aa2e0 100644
--- a/core/java/android/view/textclassifier/TextLinksParams.java
+++ b/core/java/android/view/textclassifier/TextLinksParams.java
@@ -113,7 +113,7 @@ public final class TextLinksParams {
return TextLinks.STATUS_UNSUPPORTED_CHARACTER;
}
- if (!textString.startsWith(textLinks.getText())) {
+ if (!textString.startsWith(textLinks.getText().toString())) {
return TextLinks.STATUS_DIFFERENT_TEXT;
}
if (textLinks.getLinks().isEmpty()) {
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 3fdedc88fe53..93659a4ac1eb 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -371,7 +371,9 @@ public class AccessibilityShortcutController {
// targets during boot. Needs to read settings directly here.
String shortcutTargets = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, mUserId);
- if (TextUtils.isEmpty(shortcutTargets)) {
+ // A11y warning dialog updates settings to empty string, when user disables a11y shortcut.
+ // Only fallback to default a11y service, when setting is never updated.
+ if (shortcutTargets == null) {
shortcutTargets = mContext.getString(R.string.config_defaultAccessibilityService);
}
return !TextUtils.isEmpty(shortcutTargets);
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index c979133d2493..041019ec4841 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -102,6 +102,47 @@ class AudioTrackJniStorage {
}
};
+class TunerConfigurationHelper {
+ JNIEnv *const mEnv;
+ jobject const mTunerConfiguration;
+
+ struct Ids {
+ Ids(JNIEnv *env)
+ : mClass(FindClassOrDie(env, "android/media/AudioTrack$TunerConfiguration")),
+ mContentId(GetFieldIDOrDie(env, mClass, "mContentId", "I")),
+ mSyncId(GetFieldIDOrDie(env, mClass, "mSyncId", "I")) {}
+ const jclass mClass;
+ const jfieldID mContentId;
+ const jfieldID mSyncId;
+ };
+
+ static const Ids &getIds(JNIEnv *env) {
+ // Meyer's singleton, initializes first time control passes through
+ // declaration in a block and is thread-safe per ISO/IEC 14882:2011 6.7.4.
+ static Ids ids(env);
+ return ids;
+ }
+
+public:
+ TunerConfigurationHelper(JNIEnv *env, jobject tunerConfiguration)
+ : mEnv(env), mTunerConfiguration(tunerConfiguration) {}
+
+ int32_t getContentId() const {
+ if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0;
+ const Ids &ids = getIds(mEnv);
+ return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mContentId);
+ }
+
+ int32_t getSyncId() const {
+ if (mEnv == nullptr || mTunerConfiguration == nullptr) return 0;
+ const Ids &ids = getIds(mEnv);
+ return (int32_t)mEnv->GetIntField(mTunerConfiguration, ids.mSyncId);
+ }
+
+ // optional check to confirm class and field ids can be found.
+ static void initCheckOrDie(JNIEnv *env) { (void)getIds(env); }
+};
+
static Mutex sLock;
static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
@@ -213,24 +254,36 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio
}
// ----------------------------------------------------------------------------
-static jint
-android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
- jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
- jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
- jlong nativeAudioTrack, jboolean offload) {
-
+static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
+ jobject jaa, jintArray jSampleRate,
+ jint channelPositionMask, jint channelIndexMask,
+ jint audioFormat, jint buffSizeInBytes, jint memoryMode,
+ jintArray jSession, jlong nativeAudioTrack,
+ jboolean offload, jint encapsulationMode,
+ jobject tunerConfiguration) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
- " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
- jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
- nativeAudioTrack, offload);
-
- sp<AudioTrack> lpTrack = 0;
+ " nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p",
+ jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
+ nativeAudioTrack, offload, encapsulationMode, tunerConfiguration);
if (jSession == NULL) {
ALOGE("Error creating AudioTrack: invalid session ID pointer");
return (jint) AUDIO_JAVA_ERROR;
}
+ // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
+ if (tunerConfiguration != nullptr) {
+ const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
+ ALOGE("Error creating AudioTrack: unsupported tuner contentId:%d syncId:%d",
+ tunerHelper.getContentId(), tunerHelper.getSyncId());
+ return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+ }
+ // TODO: replace when we land matching AudioTrack::set() in frameworks/av in r or r-tv-dev.
+ if (encapsulationMode != 0 /* ENCAPSULATION_MODE_NONE */) {
+ ALOGE("Error creating AudioTrack: unsupported encapsulationMode %d", encapsulationMode);
+ return (jint)AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
+ }
+
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
@@ -249,6 +302,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
}
// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
+ sp<AudioTrack> lpTrack;
if (nativeAudioTrack == 0) {
if (jaa == 0) {
ALOGE("Error creating AudioTrack: invalid audio attributes");
@@ -1304,82 +1358,75 @@ static void android_media_AudioTrack_set_delay_padding(JNIEnv *env, jobject thi
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
- // name, signature, funcPtr
- {"native_is_direct_output_supported",
- "(IIIIIII)Z",
- (void *)android_media_AudioTrack_is_direct_output_supported},
- {"native_start", "()V", (void *)android_media_AudioTrack_start},
- {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
- {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
- {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
- {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
- (void *)android_media_AudioTrack_setup},
- {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
- {"native_release", "()V", (void *)android_media_AudioTrack_release},
- {"native_write_byte", "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
- {"native_write_native_bytes",
- "(Ljava/nio/ByteBuffer;IIIZ)I",
- (void *)android_media_AudioTrack_write_native_bytes},
- {"native_write_short", "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
- {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
- {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
- {"native_get_buffer_size_frames",
- "()I", (void *)android_media_AudioTrack_get_buffer_size_frames},
- {"native_set_buffer_size_frames",
- "(I)I", (void *)android_media_AudioTrack_set_buffer_size_frames},
- {"native_get_buffer_capacity_frames",
- "()I", (void *)android_media_AudioTrack_get_buffer_capacity_frames},
- {"native_set_playback_rate",
- "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
- {"native_get_playback_rate",
- "()I", (void *)android_media_AudioTrack_get_playback_rate},
- {"native_set_playback_params",
- "(Landroid/media/PlaybackParams;)V",
- (void *)android_media_AudioTrack_set_playback_params},
- {"native_get_playback_params",
- "()Landroid/media/PlaybackParams;",
- (void *)android_media_AudioTrack_get_playback_params},
- {"native_set_marker_pos","(I)I", (void *)android_media_AudioTrack_set_marker_pos},
- {"native_get_marker_pos","()I", (void *)android_media_AudioTrack_get_marker_pos},
- {"native_set_pos_update_period",
- "(I)I", (void *)android_media_AudioTrack_set_pos_update_period},
- {"native_get_pos_update_period",
- "()I", (void *)android_media_AudioTrack_get_pos_update_period},
- {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
- {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
- {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
- {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
- {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
- {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
- {"native_getMetrics", "()Landroid/os/PersistableBundle;",
- (void *)android_media_AudioTrack_native_getMetrics},
- {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
- {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
- {"native_get_output_sample_rate",
- "(I)I", (void *)android_media_AudioTrack_get_output_sample_rate},
- {"native_get_min_buff_size",
- "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
- {"native_setAuxEffectSendLevel",
- "(F)I", (void *)android_media_AudioTrack_setAuxEffectSendLevel},
- {"native_attachAuxEffect",
- "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
- {"native_setOutputDevice", "(I)Z",
- (void *)android_media_AudioTrack_setOutputDevice},
- {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
- {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
- {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
- {"native_applyVolumeShaper",
- "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
- (void *)android_media_AudioTrack_apply_volume_shaper},
- {"native_getVolumeShaperState",
- "(I)Landroid/media/VolumeShaper$State;",
- (void *)android_media_AudioTrack_get_volume_shaper_state},
- {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
- {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
- {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
+ // name, signature, funcPtr
+ {"native_is_direct_output_supported", "(IIIIIII)Z",
+ (void *)android_media_AudioTrack_is_direct_output_supported},
+ {"native_start", "()V", (void *)android_media_AudioTrack_start},
+ {"native_stop", "()V", (void *)android_media_AudioTrack_stop},
+ {"native_pause", "()V", (void *)android_media_AudioTrack_pause},
+ {"native_flush", "()V", (void *)android_media_AudioTrack_flush},
+ {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZILjava/lang/Object;)I",
+ (void *)android_media_AudioTrack_setup},
+ {"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
+ {"native_release", "()V", (void *)android_media_AudioTrack_release},
+ {"native_write_byte", "([BIIIZ)I", (void *)android_media_AudioTrack_writeArray<jbyteArray>},
+ {"native_write_native_bytes", "(Ljava/nio/ByteBuffer;IIIZ)I",
+ (void *)android_media_AudioTrack_write_native_bytes},
+ {"native_write_short", "([SIIIZ)I",
+ (void *)android_media_AudioTrack_writeArray<jshortArray>},
+ {"native_write_float", "([FIIIZ)I",
+ (void *)android_media_AudioTrack_writeArray<jfloatArray>},
+ {"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
+ {"native_get_buffer_size_frames", "()I",
+ (void *)android_media_AudioTrack_get_buffer_size_frames},
+ {"native_set_buffer_size_frames", "(I)I",
+ (void *)android_media_AudioTrack_set_buffer_size_frames},
+ {"native_get_buffer_capacity_frames", "()I",
+ (void *)android_media_AudioTrack_get_buffer_capacity_frames},
+ {"native_set_playback_rate", "(I)I", (void *)android_media_AudioTrack_set_playback_rate},
+ {"native_get_playback_rate", "()I", (void *)android_media_AudioTrack_get_playback_rate},
+ {"native_set_playback_params", "(Landroid/media/PlaybackParams;)V",
+ (void *)android_media_AudioTrack_set_playback_params},
+ {"native_get_playback_params", "()Landroid/media/PlaybackParams;",
+ (void *)android_media_AudioTrack_get_playback_params},
+ {"native_set_marker_pos", "(I)I", (void *)android_media_AudioTrack_set_marker_pos},
+ {"native_get_marker_pos", "()I", (void *)android_media_AudioTrack_get_marker_pos},
+ {"native_set_pos_update_period", "(I)I",
+ (void *)android_media_AudioTrack_set_pos_update_period},
+ {"native_get_pos_update_period", "()I",
+ (void *)android_media_AudioTrack_get_pos_update_period},
+ {"native_set_position", "(I)I", (void *)android_media_AudioTrack_set_position},
+ {"native_get_position", "()I", (void *)android_media_AudioTrack_get_position},
+ {"native_get_latency", "()I", (void *)android_media_AudioTrack_get_latency},
+ {"native_get_underrun_count", "()I", (void *)android_media_AudioTrack_get_underrun_count},
+ {"native_get_flags", "()I", (void *)android_media_AudioTrack_get_flags},
+ {"native_get_timestamp", "([J)I", (void *)android_media_AudioTrack_get_timestamp},
+ {"native_getMetrics", "()Landroid/os/PersistableBundle;",
+ (void *)android_media_AudioTrack_native_getMetrics},
+ {"native_set_loop", "(III)I", (void *)android_media_AudioTrack_set_loop},
+ {"native_reload_static", "()I", (void *)android_media_AudioTrack_reload},
+ {"native_get_output_sample_rate", "(I)I",
+ (void *)android_media_AudioTrack_get_output_sample_rate},
+ {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioTrack_get_min_buff_size},
+ {"native_setAuxEffectSendLevel", "(F)I",
+ (void *)android_media_AudioTrack_setAuxEffectSendLevel},
+ {"native_attachAuxEffect", "(I)I", (void *)android_media_AudioTrack_attachAuxEffect},
+ {"native_setOutputDevice", "(I)Z", (void *)android_media_AudioTrack_setOutputDevice},
+ {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
+ {"native_enableDeviceCallback", "()V",
+ (void *)android_media_AudioTrack_enableDeviceCallback},
+ {"native_disableDeviceCallback", "()V",
+ (void *)android_media_AudioTrack_disableDeviceCallback},
+ {"native_applyVolumeShaper",
+ "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
+ (void *)android_media_AudioTrack_apply_volume_shaper},
+ {"native_getVolumeShaperState", "(I)Landroid/media/VolumeShaper$State;",
+ (void *)android_media_AudioTrack_get_volume_shaper_state},
+ {"native_setPresentation", "(II)I", (void *)android_media_AudioTrack_setPresentation},
+ {"native_getPortId", "()I", (void *)android_media_AudioTrack_get_port_id},
+ {"native_set_delay_padding", "(II)V", (void *)android_media_AudioTrack_set_delay_padding},
};
-
// field names found in android/media/AudioTrack.java
#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
@@ -1436,6 +1483,10 @@ int register_android_media_AudioTrack(JNIEnv *env)
gPlaybackParamsFields.init(env);
gVolumeShaperFields.init(env);
+
+ // optional check that the TunerConfiguration class and fields exist.
+ TunerConfigurationHelper::initCheckOrDie(env);
+
return res;
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 573f378f57b1..50a60a917185 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -826,13 +826,6 @@ static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token));
}
-static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj, jint id) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return JNI_FALSE;
- status_t err = SurfaceComposerClient::setActiveConfig(token, static_cast<int>(id));
- return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
-}
-
static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return NULL;
@@ -1400,8 +1393,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetDisplayConfigs },
{"nativeGetActiveConfig", "(Landroid/os/IBinder;)I",
(void*)nativeGetActiveConfig },
- {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z",
- (void*)nativeSetActiveConfig },
{"nativeSetDesiredDisplayConfigSpecs",
"(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayConfigSpecs;)Z",
(void*)nativeSetDesiredDisplayConfigSpecs },
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0fca1d19c0e5..0ae11a106a54 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -155,4 +155,5 @@ enum EventId {
SET_AUTO_TIME_ZONE = 128;
SET_PACKAGES_PROTECTED = 129;
SET_FACTORY_RESET_PROTECTION = 130;
+ SET_COMMON_CRITERIA_MODE = 131;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f8c51666d19a..6b27348b6d87 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -131,7 +131,6 @@
<protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
- <protected-broadcast android:name="android.app.backup.intent.RUN" />
<protected-broadcast android:name="android.app.backup.intent.CLEAR" />
<protected-broadcast android:name="android.app.backup.intent.INIT" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a78195b05d8f..2585197d85ec 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3545,6 +3545,12 @@
<!-- Whether the device supports quick settings and its associated APIs -->
<bool name="config_quickSettingsSupported">true</bool>
+ <!-- Comma separated list of extra quick settings tiles to be added to the default set as
+ defined in SystemUi (com.android.systemui.R.string.quick_settings_tiles_default).
+ Custom tiles (TileService) must be specified as "custom(pkg_name/class_in_package)"
+ (without the quotes, both absolute and relative class works). -->
+ <string name="config_defaultExtraQuickSettingsTiles" translatable="false"></string>
+
<!-- The component name, flattened to a string, for the default autofill service
to enabled for an user. This service must be trusted, as it can be activated
without explicit consent of the user. If no autofill service with the
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 669b41e53ba1..379d0aa3da3d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3409,6 +3409,7 @@
<java-symbol type="string" name="etws_primary_default_message_others" />
<java-symbol type="bool" name="config_quickSettingsSupported" />
+ <java-symbol type="string" name="config_defaultExtraQuickSettingsTiles" />
<java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" />
diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
new file mode 100644
index 000000000000..21259cc81758
--- /dev/null
+++ b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.SearchResultProto;
+
+import org.junit.Test;
+
+@SmallTest
+public class SearchResultsTest {
+
+ @Test
+ public void testSearchResultsEqual() {
+ final String uri = "testUri";
+ final String schemaType = "testSchema";
+ SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .build())
+ .build();
+ SearchResultProto searchResults1 = SearchResultProto.newBuilder()
+ .addResults(result1)
+ .build();
+ SearchResults res1 = new SearchResults(searchResults1);
+ SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder()
+ .setDocument(DocumentProto.newBuilder()
+ .setUri(uri)
+ .setSchema(schemaType)
+ .build())
+ .build();
+ SearchResultProto searchResults2 = SearchResultProto.newBuilder()
+ .addResults(result2)
+ .build();
+ SearchResults res2 = new SearchResults(searchResults2);
+ assertThat(res1.toString()).isEqualTo(res2.toString());
+ }
+
+ @Test
+ public void buildSearchSpecWithoutTermMatchType() {
+ assertThrows(RuntimeException.class, () -> SearchSpec.newBuilder()
+ .setSchemaTypes("testSchemaType")
+ .build());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 1410f4f1bf72..09ea1b1865c0 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -124,7 +124,7 @@ public class TransactionExecutorTests {
assertArrayEquals(new int[] {}, path(ON_START));
assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
- assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+ assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
}
@@ -362,7 +362,9 @@ public class TransactionExecutorTests {
public void testClosestStateResolutionFromOnStart() {
mClientRecord.setState(ON_START);
assertEquals(ON_RESUME, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
- new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY})));
+ new int[] {ON_CREATE, ON_RESUME, ON_PAUSE, ON_DESTROY})));
+ assertEquals(ON_STOP, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
+ new int[] {ON_STOP})));
assertEquals(ON_CREATE, mExecutorHelper.getClosestOfStates(mClientRecord, shuffledArray(
new int[] {ON_CREATE})));
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index e23c51e66a02..8e2490789a6f 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -46,6 +46,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -193,4 +194,15 @@ public class AccessibilityManagerTest {
}
});
}
+
+ @Test
+ public void testSetWindowMagnificationConnection() throws Exception {
+ AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+ IWindowMagnificationConnection connection = Mockito.mock(
+ IWindowMagnificationConnection.class);
+
+ manager.setWindowMagnificationConnection(connection);
+
+ verify(mMockService).setWindowMagnificationConnection(connection);
+ }
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 0ced68ef8695..4dbc79b54199 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -213,6 +213,36 @@ public class AudioTrack extends PlayerBase
private final static String TAG = "android.media.AudioTrack";
+ /** @hide */
+ @IntDef({
+ ENCAPSULATION_MODE_NONE,
+ ENCAPSULATION_MODE_ELEMENTARY_STREAM,
+ ENCAPSULATION_MODE_HANDLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncapsulationMode {}
+
+ // Important: The ENCAPSULATION_MODE values must be kept in sync with native header files.
+ /**
+ * This mode indicates no metadata encapsulation,
+ * which is the default mode for sending audio data
+ * through {@code AudioTrack}.
+ */
+ public static final int ENCAPSULATION_MODE_NONE = 0;
+ /**
+ * This mode indicates metadata encapsulation with an elementary stream payload.
+ * Both compressed and PCM format is allowed.
+ *
+ * TODO(b/147778408) Link: See the Android developers guide for more information.
+ */
+ public static final int ENCAPSULATION_MODE_ELEMENTARY_STREAM = 1;
+ /**
+ * This mode indicates metadata encapsulation with a handle payload.
+ * The handle is a 64 bit long, provided by the Tuner API.
+ *
+ * TODO(b/147778408) Link: Fill in Tuner API to obtain the handle.
+ */
+ public static final int ENCAPSULATION_MODE_HANDLE = 2;
/** @hide */
@IntDef({
@@ -592,11 +622,13 @@ public class AudioTrack extends PlayerBase
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
- this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
+ this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/,
+ ENCAPSULATION_MODE_NONE, null /* tunerConfiguration */);
}
private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
- int mode, int sessionId, boolean offload)
+ int mode, int sessionId, boolean offload, int encapsulationMode,
+ @Nullable TunerConfiguration tunerConfiguration)
throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
// mState already == STATE_UNINITIALIZED
@@ -663,7 +695,7 @@ public class AudioTrack extends PlayerBase
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
- offload);
+ offload, encapsulationMode, tunerConfiguration);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -672,6 +704,8 @@ public class AudioTrack extends PlayerBase
mSampleRate = sampleRate[0];
mSessionId = session[0];
+ // TODO: consider caching encapsulationMode and tunerConfiguration in the Java object.
+
if ((mAttributes.getFlags() & AudioAttributes.FLAG_HW_AV_SYNC) != 0) {
int frameSizeInBytes;
if (AudioFormat.isEncodingLinearFrames(mAudioFormat)) {
@@ -745,7 +779,9 @@ public class AudioTrack extends PlayerBase
0 /*mDataLoadMode - NA*/,
session,
nativeTrackInJavaObj,
- false /*offload*/);
+ false /*offload*/,
+ ENCAPSULATION_MODE_NONE,
+ null /* tunerConfiguration */);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -758,6 +794,99 @@ public class AudioTrack extends PlayerBase
}
/**
+ * TunerConfiguration is used to convey tuner information
+ * from the android.media.tv.Tuner API to AudioTrack construction.
+ *
+ * Use the Builder to construct the TunerConfiguration object,
+ * which is then used by the {@link AudioTrack.Builder} to create an AudioTrack.
+ */
+ public static class TunerConfiguration {
+ private final int mContentId;
+ private final int mSyncId;
+
+ private TunerConfiguration(int contentId, int syncId) {
+ mContentId = contentId;
+ mSyncId = syncId;
+ }
+
+ /**
+ * Returns the contentId.
+ */
+ public int getContentId() {
+ return mContentId;
+ }
+
+ /**
+ * Returns the syncId.
+ */
+ public int getSyncId() {
+ return mSyncId;
+ }
+
+ /**
+ * Builder class for {@link AudioTrack.TunerConfiguration} objects.
+ */
+ public static class Builder {
+ private int mContentId;
+ private int mSyncId;
+
+ /**
+ * Sets the contentId from the Tuner filter.
+ *
+ * @param contentId selects the audio stream to use.
+ * See android.media.tv.tuner.filter.Filter#getId().
+ * This is always a positive number.
+ * TODO(b/147778408) Link to tuner filter doc when unhidden.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) {
+ if (contentId < 1) {
+ throw new IllegalArgumentException(
+ "contentId " + contentId + " must be positive");
+ }
+ mContentId = contentId;
+ return this;
+ }
+
+ /**
+ * Sets the syncId from the Tuner filter.
+ *
+ * @param syncId selects the clock to use for synchronization
+ * of audio with other streams such as video.
+ * See android.media.tv.tuner.Tuner#getAvSyncHwId().
+ * This is always a positive number.
+ * TODO(b/147778408) Link to tuner filter doc when unhidden.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) {
+ if (syncId < 1) {
+ throw new IllegalArgumentException("syncId " + syncId + " must be positive");
+ }
+ mSyncId = syncId;
+ return this;
+ }
+
+ /**
+ * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with
+ * the parameters set on this {@code Builder}.
+ *
+ * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}.
+ * @throws UnsupportedOperationException if the parameters set on the
+ * {@code Builder} are incompatible.
+ */
+ public @NonNull TunerConfiguration build() {
+ if (mContentId < 1 || mSyncId < 1) {
+ throw new UnsupportedOperationException(
+ "contentId " + mContentId
+ + " syncId " + mSyncId
+ + " must be set");
+ }
+ return new TunerConfiguration(mContentId, mSyncId);
+ }
+ }
+ }
+
+ /**
* Builder class for {@link AudioTrack} objects.
* Use this class to configure and create an <code>AudioTrack</code> instance. By setting audio
* attributes and audio format parameters, you indicate which of those vary from the default
@@ -799,10 +928,12 @@ public class AudioTrack extends PlayerBase
private AudioAttributes mAttributes;
private AudioFormat mFormat;
private int mBufferSizeInBytes;
+ private int mEncapsulationMode = ENCAPSULATION_MODE_NONE;
private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
private int mMode = MODE_STREAM;
private int mPerformanceMode = PERFORMANCE_MODE_NONE;
private boolean mOffload = false;
+ private TunerConfiguration mTunerConfiguration;
/**
* Constructs a new Builder with the default values as described above.
@@ -869,6 +1000,34 @@ public class AudioTrack extends PlayerBase
}
/**
+ * Sets the encapsulation mode.
+ *
+ * Encapsulation mode allows metadata to be sent together with
+ * the audio data payload in a {@code ByteBuffer}.
+ * The data format is specified in the Android developers site.
+ *
+ * TODO(b/147778408) Link to doc page.
+ *
+ * @param encapsulationMode one of {@link AudioTrack#ENCAPSULATION_MODE_NONE},
+ * {@link AudioTrack#ENCAPSULATION_MODE_ELEMENTARY_STREAM},
+ * {@link AudioTrack#ENCAPSULATION_MODE_HANDLE}.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setEncapsulationMode(@EncapsulationMode int encapsulationMode) {
+ switch (encapsulationMode) {
+ case ENCAPSULATION_MODE_NONE:
+ case ENCAPSULATION_MODE_ELEMENTARY_STREAM:
+ case ENCAPSULATION_MODE_HANDLE:
+ mEncapsulationMode = encapsulationMode;
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Invalid encapsulation mode " + encapsulationMode);
+ }
+ return this;
+ }
+
+ /**
* Sets the mode under which buffers of audio data are transferred from the
* {@link AudioTrack} to the framework.
* @param mode one of {@link AudioTrack#MODE_STREAM}, {@link AudioTrack#MODE_STATIC}.
@@ -949,6 +1108,25 @@ public class AudioTrack extends PlayerBase
}
/**
+ * Sets the tuner configuration for the {@code AudioTrack}.
+ *
+ * The {@link AudioTrack.TunerConfiguration} consists of parameters obtained from
+ * the Android TV tuner API which indicate the audio content stream id and the
+ * synchronization id for the {@code AudioTrack}.
+ *
+ * @param tunerConfiguration obtained by {@link AudioTrack.TunerConfiguration.Builder}.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setTunerConfiguration(
+ @NonNull TunerConfiguration tunerConfiguration) {
+ if (tunerConfiguration == null) {
+ throw new IllegalArgumentException("tunerConfiguration is null");
+ }
+ mTunerConfiguration = tunerConfiguration;
+ return this;
+ }
+
+ /**
* Builds an {@link AudioTrack} instance initialized with all the parameters set
* on this <code>Builder</code>.
* @return a new successfully initialized {@link AudioTrack} instance.
@@ -1003,6 +1181,8 @@ public class AudioTrack extends PlayerBase
}
}
+ // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc?
+
try {
// If the buffer size is not specified in streaming mode,
// use a single frame for the buffer size and let the
@@ -1012,7 +1192,8 @@ public class AudioTrack extends PlayerBase
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioTrack track = new AudioTrack(
- mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
+ mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload,
+ mEncapsulationMode, mTunerConfiguration);
if (track.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -3595,7 +3776,7 @@ public class AudioTrack extends PlayerBase
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
- boolean offload);
+ boolean offload, int encapsulationMode, Object tunerConfiguration);
private native final void native_finalize();
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index aa38e5148069..a25aff6611ca 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -18,13 +18,15 @@ package android.media;
import android.content.Intent;
import android.media.IMediaRoute2ProviderClient;
+import android.os.Bundle;
/**
* {@hide}
*/
oneway interface IMediaRoute2Provider {
void setClient(IMediaRoute2ProviderClient client);
- void requestCreateSession(String packageName, String routeId, long requestId);
+ void requestCreateSession(String packageName, String routeId, long requestId,
+ in @nullable Bundle sessionHints);
void releaseSession(String sessionId);
void selectRoute(String sessionId, String routeId);
diff --git a/media/java/android/media/IMediaRouter2Manager.aidl b/media/java/android/media/IMediaRouter2Manager.aidl
index e9add1756224..ffad6592e902 100644
--- a/media/java/android/media/IMediaRouter2Manager.aidl
+++ b/media/java/android/media/IMediaRouter2Manager.aidl
@@ -18,12 +18,14 @@ package android.media;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2Info;
+import android.media.RoutingSessionInfo;
/**
* {@hide}
*/
oneway interface IMediaRouter2Manager {
- void notifyRouteSelected(String packageName, in MediaRoute2Info route);
+ void notifySessionCreated(in RoutingSessionInfo sessionInfo);
+ void notifySessionsUpdated();
void notifyPreferredFeaturesChanged(String packageName, in List<String> preferredFeatures);
void notifyRoutesAdded(in List<MediaRoute2Info> routes);
void notifyRoutesRemoved(in List<MediaRoute2Info> routes);
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 2d3e185e967c..281e7c6b6f68 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -24,6 +24,7 @@ import android.media.MediaRoute2Info;
import android.media.MediaRouterClientState;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
/**
* {@hide}
@@ -52,7 +53,8 @@ interface IMediaRouterService {
void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
- void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId);
+ void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
+ in @nullable Bundle sessionHints);
void setDiscoveryRequest2(IMediaRouter2Client client, in RouteDiscoveryPreference preference);
void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
@@ -71,4 +73,12 @@ interface IMediaRouterService {
in MediaRoute2Info route, int direction);
List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
+ void selectClientRoute(IMediaRouter2Manager manager,
+ String sessionId, in MediaRoute2Info route);
+ void deselectClientRoute(IMediaRouter2Manager manager,
+ String sessionId, in MediaRoute2Info route);
+ void transferToClientRoute(IMediaRouter2Manager manager,
+ String sessionId, in MediaRoute2Info route);
+ void releaseClientSession(IMediaRouter2Manager manager, String sessionId);
+
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 5a3de6d60277..6bfa851804a1 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -161,8 +161,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param sessionInfo information of the new session.
* The {@link RoutingSessionInfo#getId() id} of the session must be unique.
* @param requestId id of the previous request to create this session provided in
- * {@link #onCreateSession(String, String, long)}
- * @see #onCreateSession(String, String, long)
+ * {@link #onCreateSession(String, String, long, Bundle)}
+ * @see #onCreateSession(String, String, long, Bundle)
* @hide
*/
public final void notifySessionCreated(@NonNull RoutingSessionInfo sessionInfo,
@@ -177,7 +177,6 @@ public abstract class MediaRoute2ProviderService extends Service {
}
mSessionInfo.put(sessionInfo.getId(), sessionInfo);
}
- schedulePublishState();
if (mClient == null) {
return;
@@ -196,8 +195,8 @@ public abstract class MediaRoute2ProviderService extends Service {
* Notifies clients of that the session could not be created.
*
* @param requestId id of the previous request to create the session provided in
- * {@link #onCreateSession(String, String, long)}.
- * @see #onCreateSession(String, String, long)
+ * {@link #onCreateSession(String, String, long, Bundle)}.
+ * @see #onCreateSession(String, String, long, Bundle)
* @hide
*/
public final void notifySessionCreationFailed(long requestId) {
@@ -290,6 +289,9 @@ public abstract class MediaRoute2ProviderService extends Service {
* @param packageName the package name of the application that selected the route
* @param routeId the id of the route initially being connected
* @param requestId the id of this session creation request
+ * @param sessionHints an optional bundle of app-specific arguments sent by
+ * {@link MediaRouter2}, or null if none. The contents of this bundle
+ * may affect the result of session creation.
*
* @see RoutingSessionInfo.Builder#Builder(String, String)
* @see RoutingSessionInfo.Builder#addSelectedRoute(String)
@@ -297,7 +299,7 @@ public abstract class MediaRoute2ProviderService extends Service {
* @hide
*/
public abstract void onCreateSession(@NonNull String packageName, @NonNull String routeId,
- long requestId);
+ long requestId, @Nullable Bundle sessionHints);
/**
* Called when the session should be released. A client of the session or system can request
@@ -432,12 +434,14 @@ public abstract class MediaRoute2ProviderService extends Service {
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle requestCreateSession) {
if (!checkCallerisSystem()) {
return;
}
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onCreateSession,
- MediaRoute2ProviderService.this, packageName, routeId, requestId));
+ MediaRoute2ProviderService.this, packageName, routeId, requestId,
+ requestCreateSession));
}
@Override
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 971b08d17cdb..51d08ec96e6f 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -92,6 +92,7 @@ public class MediaRouter2 {
@GuardedBy("sRouterLock")
private boolean mShouldUpdateRoutes;
private volatile List<MediaRoute2Info> mFilteredRoutes = Collections.emptyList();
+ private volatile OnCreateSessionListener mOnCreateSessionListener;
/**
* Gets an instance of the media router associated with the context.
@@ -281,6 +282,19 @@ public class MediaRouter2 {
}
/**
+ * Sets an {@link OnCreateSessionListener} to send hints when creating a session.
+ * To send the hints, listener should be set <em>BEFORE</em> calling
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ *
+ * @param listener A listener to send optional app-specific hints when creating a session.
+ * {@code null} for unset.
+ * @hide
+ */
+ public void setOnCreateSessionListener(@Nullable OnCreateSessionListener listener) {
+ mOnCreateSessionListener = listener;
+ }
+
+ /**
* Requests the media route provider service to create a session with the given route.
*
* @param route the route you want to create a session with.
@@ -300,13 +314,24 @@ public class MediaRouter2 {
SessionCreationRequest request = new SessionCreationRequest(requestId, route);
mSessionCreationRequests.add(request);
+
+ OnCreateSessionListener listener = mOnCreateSessionListener;
+ Bundle sessionHints = null;
+ if (listener != null) {
+ sessionHints = listener.onCreateSession(route);
+ if (sessionHints != null) {
+ sessionHints = new Bundle(sessionHints);
+ }
+ }
+
Client2 client;
synchronized (sRouterLock) {
client = mClient;
}
if (client != null) {
try {
- mMediaRouterService.requestCreateSession(client, route, requestId);
+ mMediaRouterService.requestCreateSession(client, route, requestId,
+ sessionHints);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to request to create session.", ex);
mHandler.sendMessage(obtainMessage(MediaRouter2::createControllerOnHandler,
@@ -707,6 +732,34 @@ public class MediaRouter2 {
}
/**
+ * A listener interface to send an optional app-specific hints when creating a session.
+ *
+ * @hide
+ */
+ public interface OnCreateSessionListener {
+ /**
+ * Called when the {@link MediaRouter2} is about to request
+ * the media route provider service to create a session with the given route.
+ * The {@link Bundle} returned here will be sent to media route provider service as a hint
+ * for creating a session.
+ * <p>
+ * To send hints when creating the session, set this listener before calling
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ * <p>
+ * This will be called on the same thread which calls
+ * {@link #requestCreateSession(MediaRoute2Info)}.
+ *
+ * @param route The route to create session with
+ * @return An optional bundle of app-specific arguments to send to the provider,
+ * or null if none. The contents of this bundle may affect the result of
+ * session creation.
+ * @see MediaRoute2ProviderService#onCreateSession(String, String, long, Bundle)
+ */
+ @Nullable
+ Bundle onCreateSession(@NonNull MediaRoute2Info route);
+ }
+
+ /**
* A class to control media routing session in media route provider.
* For example, selecting/deselcting/transferring routes to session can be done through this
* class. Instances are created by {@link MediaRouter2}.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 70229335905b..5cb32d6ab550 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -22,6 +22,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -131,7 +132,7 @@ public class MediaRouter2Manager {
Objects.requireNonNull(callback, "callback must not be null");
if (!mCallbackRecords.remove(new CallbackRecord(null, callback))) {
- Log.w(TAG, "Ignore removing unknown callback. " + callback);
+ Log.w(TAG, "unregisterCallback: Ignore unknown callback. " + callback);
return;
}
@@ -175,6 +176,29 @@ public class MediaRouter2Manager {
return routes;
}
+ /**
+ * Gets routing controllers of an application with the given package name.
+ * If the application isn't running or it doesn't use {@link MediaRouter2}, an empty list
+ * will be returned.
+ */
+ @NonNull
+ public List<RoutingController> getRoutingControllers(@NonNull String packageName) {
+ Objects.requireNonNull(packageName, "packageName must not be null");
+
+ List<RoutingController> controllers = new ArrayList<>();
+
+ for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
+ if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
+ controllers.add(new RoutingController(sessionInfo));
+ }
+ }
+ return controllers;
+ }
+
+ /**
+ * Gets the list of all active routing sessions. It doesn't include default routing sessions
+ * of applications.
+ */
@NonNull
public List<RoutingSessionInfo> getActiveSessions() {
Client client;
@@ -192,23 +216,7 @@ public class MediaRouter2Manager {
}
/**
- * Gets the list of routes that are actively used by {@link MediaRouter2}.
- */
- @NonNull
- public List<MediaRoute2Info> getActiveRoutes() {
- List<MediaRoute2Info> routes = new ArrayList<>();
- synchronized (mRoutesLock) {
- for (MediaRoute2Info route : mRoutes.values()) {
- if (!TextUtils.isEmpty(route.getClientPackageName())) {
- routes.add(route);
- }
- }
- }
- return routes;
- }
-
- /**
- * Gets the list of discovered routes
+ * Gets the list of all discovered routes
*/
@NonNull
public List<MediaRoute2Info> getAllRoutes() {
@@ -222,6 +230,10 @@ public class MediaRouter2Manager {
/**
* Selects media route for the specified package name.
*
+ * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable
+ * route} of a routing session of the application, the session will be transferred to
+ * the route. If not, a new routing session will be created.
+ *
* @param packageName the package name of the application that should change it's media route
* @param route the route to be selected.
*/
@@ -229,6 +241,13 @@ public class MediaRouter2Manager {
Objects.requireNonNull(packageName, "packageName must not be null");
Objects.requireNonNull(route, "route must not be null");
+ for (RoutingController controller : getRoutingControllers(packageName)) {
+ if (controller.getSessionInfo().getTransferrableRoutes().contains(route.getId())) {
+ controller.transferToRoute(route);
+ return;
+ }
+ }
+
Client client;
synchronized (sLock) {
client = mClient;
@@ -238,6 +257,7 @@ public class MediaRouter2Manager {
int requestId = mNextRequestId.getAndIncrement();
mMediaRouterService.requestCreateClientSession(
client, packageName, route, requestId);
+ //TODO: release the previous session?
} catch (RemoteException ex) {
Log.e(TAG, "Unable to select media route", ex);
}
@@ -245,7 +265,7 @@ public class MediaRouter2Manager {
}
/**
- * Requests a volume change for the route asynchronously.
+ * Requests a volume change for a route asynchronously.
* <p>
* It may have no effect if the route is currently not selected.
* </p>
@@ -346,9 +366,16 @@ public class MediaRouter2Manager {
}
}
- void notifyRouteSelected(String packageName, MediaRoute2Info route) {
+ void notifySessionCreated(RoutingSessionInfo sessionInfo) {
for (CallbackRecord record : mCallbackRecords) {
- record.mExecutor.execute(() -> record.mCallback.onRouteSelected(packageName, route));
+ record.mExecutor.execute(() -> record.mCallback.onSessionCreated(
+ new RoutingController(sessionInfo)));
+ }
+ }
+
+ void notifySessionInfosChanged() {
+ for (CallbackRecord record : mCallbackRecords) {
+ record.mExecutor.execute(() -> record.mCallback.onSessionsUpdated());
}
}
@@ -365,6 +392,275 @@ public class MediaRouter2Manager {
}
/**
+ * @hide
+ */
+ public RoutingController getControllerForSession(@NonNull RoutingSessionInfo sessionInfo) {
+ return new RoutingController(sessionInfo);
+ }
+
+ /**
+ * A class to control media routing session in media route provider.
+ * With routing controller, an application can select a route into the session or deselect
+ * a route in the session.
+ */
+ public final class RoutingController {
+ private final Object mControllerLock = new Object();
+ @GuardedBy("mControllerLock")
+ private RoutingSessionInfo mSessionInfo;
+
+ RoutingController(@NonNull RoutingSessionInfo sessionInfo) {
+ mSessionInfo = sessionInfo;
+ }
+
+ /**
+ * Gets the ID of the session
+ */
+ @NonNull
+ public String getSessionId() {
+ synchronized (mControllerLock) {
+ return mSessionInfo.getId();
+ }
+ }
+
+ /**
+ * Gets the client package name of the session
+ */
+ @NonNull
+ public String getClientPackageName() {
+ synchronized (mControllerLock) {
+ return mSessionInfo.getClientPackageName();
+ }
+ }
+
+ /**
+ * @return the control hints used to control route session if available.
+ */
+ @Nullable
+ public Bundle getControlHints() {
+ synchronized (mControllerLock) {
+ return mSessionInfo.getControlHints();
+ }
+ }
+
+ /**
+ * @return the unmodifiable list of currently selected routes
+ */
+ @NonNull
+ public List<MediaRoute2Info> getSelectedRoutes() {
+ List<String> routeIds;
+ synchronized (mControllerLock) {
+ routeIds = mSessionInfo.getSelectedRoutes();
+ }
+ return getRoutesWithIds(routeIds);
+ }
+
+ /**
+ * @return the unmodifiable list of selectable routes for the session.
+ */
+ @NonNull
+ public List<MediaRoute2Info> getSelectableRoutes() {
+ List<String> routeIds;
+ synchronized (mControllerLock) {
+ routeIds = mSessionInfo.getSelectableRoutes();
+ }
+ return getRoutesWithIds(routeIds);
+ }
+
+ /**
+ * @return the unmodifiable list of deselectable routes for the session.
+ */
+ @NonNull
+ public List<MediaRoute2Info> getDeselectableRoutes() {
+ List<String> routeIds;
+ synchronized (mControllerLock) {
+ routeIds = mSessionInfo.getDeselectableRoutes();
+ }
+ return getRoutesWithIds(routeIds);
+ }
+
+ /**
+ * @return the unmodifiable list of transferrable routes for the session.
+ */
+ @NonNull
+ public List<MediaRoute2Info> getTransferrableRoutes() {
+ List<String> routeIds;
+ synchronized (mControllerLock) {
+ routeIds = mSessionInfo.getTransferrableRoutes();
+ }
+ return getRoutesWithIds(routeIds);
+ }
+
+ /**
+ * Selects a route for the remote session. The given route must satisfy all of the
+ * following conditions:
+ * <ul>
+ * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
+ * <li>ID should be included in {@link #getSelectableRoutes()}</li>
+ * </ul>
+ * If the route doesn't meet any of above conditions, it will be ignored.
+ *
+ * @see #getSelectedRoutes()
+ * @see #getSelectableRoutes()
+ */
+ public void selectRoute(@NonNull MediaRoute2Info route) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ RoutingSessionInfo sessionInfo;
+ synchronized (mControllerLock) {
+ sessionInfo = mSessionInfo;
+ }
+ if (sessionInfo.getSelectedRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring selecting a route that is already selected. route=" + route);
+ return;
+ }
+
+ if (!sessionInfo.getSelectableRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring selecting a non-selectable route=" + route);
+ return;
+ }
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.selectClientRoute(mClient, getSessionId(), route);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to select route for session.", ex);
+ }
+ }
+ }
+
+ /**
+ * Deselects a route from the remote session. The given route must satisfy all of the
+ * following conditions:
+ * <ul>
+ * <li>ID should be included in {@link #getSelectedRoutes()}</li>
+ * <li>ID should be included in {@link #getDeselectableRoutes()}</li>
+ * </ul>
+ * If the route doesn't meet any of above conditions, it will be ignored.
+ *
+ * @see #getSelectedRoutes()
+ * @see #getDeselectableRoutes()
+ */
+ public void deselectRoute(@NonNull MediaRoute2Info route) {
+ Objects.requireNonNull(route, "route must not be null");
+ RoutingSessionInfo sessionInfo;
+ synchronized (mControllerLock) {
+ sessionInfo = mSessionInfo;
+ }
+
+ if (!sessionInfo.getSelectedRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring deselecting a route that is not selected. route=" + route);
+ return;
+ }
+
+ if (!sessionInfo.getDeselectableRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring deselecting a non-deselectable route=" + route);
+ return;
+ }
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.deselectClientRoute(mClient, getSessionId(), route);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to remove route from session.", ex);
+ }
+ }
+ }
+
+ /**
+ * Transfers to a given route for the remote session. The given route must satisfy
+ * all of the following conditions:
+ * <ul>
+ * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
+ * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+ * </ul>
+ * If the route doesn't meet any of above conditions, it will be ignored.
+ *
+ * @see #getSelectedRoutes()
+ * @see #getTransferrableRoutes()
+ */
+ public void transferToRoute(@NonNull MediaRoute2Info route) {
+ Objects.requireNonNull(route, "route must not be null");
+ RoutingSessionInfo sessionInfo;
+ synchronized (mControllerLock) {
+ sessionInfo = mSessionInfo;
+ }
+
+ if (sessionInfo.getSelectedRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a route that is already added. route="
+ + route);
+ return;
+ }
+
+ if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+ Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+ return;
+ }
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.transferToClientRoute(mClient, getSessionId(), route);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to transfer to route for session.", ex);
+ }
+ }
+ }
+
+ /**
+ * Release this session.
+ * Any operation on this session after calling this method will be ignored.
+ */
+ public void release() {
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.releaseClientSession(mClient, getSessionId());
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to notify of controller release", ex);
+ }
+ }
+ }
+
+ /**
+ * Gets the session info of the session
+ * @hide
+ */
+ @NonNull
+ public RoutingSessionInfo getSessionInfo() {
+ synchronized (mControllerLock) {
+ return mSessionInfo;
+ }
+ }
+
+ private List<MediaRoute2Info> getRoutesWithIds(List<String> routeIds) {
+ List<MediaRoute2Info> routes = new ArrayList<>();
+ synchronized (mRoutesLock) {
+ for (String routeId : routeIds) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route != null) {
+ routes.add(route);
+ }
+ }
+ }
+ return Collections.unmodifiableList(routes);
+ }
+ }
+
+ /**
* Interface for receiving events about media routing changes.
*/
public static class Callback {
@@ -388,14 +684,17 @@ public class MediaRouter2Manager {
public void onRoutesChanged(@NonNull List<MediaRoute2Info> routes) {}
/**
- * Called when a route is selected for an application.
+ * Called when a routing session is created.
*
- * @param packageName the package name of the application
- * @param route the selected route of the application.
- * It is null if the application has no selected route.
+ * @param controller the controller to control the created session
*/
- public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {}
+ public void onSessionCreated(@NonNull RoutingController controller) {}
+ /**
+ * Called when at least one session info is changed.
+ * Call {@link #getActiveSessions()} to get current active session info.
+ */
+ public void onSessionsUpdated() {}
/**
* Called when the preferred route features of an app is changed.
@@ -435,11 +734,19 @@ public class MediaRouter2Manager {
class Client extends IMediaRouter2Manager.Stub {
@Override
- public void notifyRouteSelected(String packageName, MediaRoute2Info route) {
- mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected,
- MediaRouter2Manager.this, packageName, route));
+ public void notifySessionCreated(RoutingSessionInfo sessionInfo) {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionCreated,
+ MediaRouter2Manager.this, sessionInfo));
}
+ @Override
+ public void notifySessionsUpdated() {
+ mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifySessionInfosChanged,
+ MediaRouter2Manager.this));
+ // do nothing
+ }
+
+ @Override
public void notifyPreferredFeaturesChanged(String packageName, List<String> features) {
mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updatePreferredFeatures,
MediaRouter2Manager.this, packageName, features));
diff --git a/media/java/android/media/tv/tuner/Descrambler.java b/media/java/android/media/tv/tuner/Descrambler.java
index 0143582ab815..23016e9239ca 100644
--- a/media/java/android/media/tv/tuner/Descrambler.java
+++ b/media/java/android/media/tv/tuner/Descrambler.java
@@ -34,7 +34,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public class Descrambler implements AutoCloseable {
/** @hide */
- @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMPT})
+ @IntDef(prefix = "PID_TYPE_", value = {PID_TYPE_T, PID_TYPE_MMTP})
@Retention(RetentionPolicy.SOURCE)
public @interface PidType {}
@@ -45,7 +45,7 @@ public class Descrambler implements AutoCloseable {
/**
* Packet ID is used to specify packets in MMTP.
*/
- public static final int PID_TYPE_MMPT = 2;
+ public static final int PID_TYPE_MMTP = 2;
private long mNativeContext;
diff --git a/media/java/android/media/tv/tuner/Lnb.java b/media/java/android/media/tv/tuner/Lnb.java
index 8e579bf897dd..a9a15d97e859 100644
--- a/media/java/android/media/tv/tuner/Lnb.java
+++ b/media/java/android/media/tv/tuner/Lnb.java
@@ -40,7 +40,8 @@ import java.lang.annotation.RetentionPolicy;
@SystemApi
public class Lnb implements AutoCloseable {
/** @hide */
- @IntDef({VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
+ @IntDef(prefix = "VOLTAGE_",
+ value = {VOLTAGE_NONE, VOLTAGE_5V, VOLTAGE_11V, VOLTAGE_12V, VOLTAGE_13V, VOLTAGE_14V,
VOLTAGE_15V, VOLTAGE_18V, VOLTAGE_19V})
@Retention(RetentionPolicy.SOURCE)
public @interface Voltage {}
@@ -83,7 +84,8 @@ public class Lnb implements AutoCloseable {
public static final int VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
/** @hide */
- @IntDef({TONE_NONE, TONE_CONTINUOUS})
+ @IntDef(prefix = "TONE_",
+ value = {TONE_NONE, TONE_CONTINUOUS})
@Retention(RetentionPolicy.SOURCE)
public @interface Tone {}
@@ -97,7 +99,8 @@ public class Lnb implements AutoCloseable {
public static final int TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
/** @hide */
- @IntDef({POSITION_UNDEFINED, POSITION_A, POSITION_B})
+ @IntDef(prefix = "POSITION_",
+ value = {POSITION_UNDEFINED, POSITION_A, POSITION_B})
@Retention(RetentionPolicy.SOURCE)
public @interface Position {}
@@ -114,6 +117,37 @@ public class Lnb implements AutoCloseable {
*/
public static final int POSITION_B = Constants.LnbPosition.POSITION_B;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "EVENT_TYPE_",
+ value = {EVENT_TYPE_DISEQC_RX_OVERFLOW, EVENT_TYPE_DISEQC_RX_TIMEOUT,
+ EVENT_TYPE_DISEQC_RX_PARITY_ERROR, EVENT_TYPE_LNB_OVERLOAD})
+ public @interface EventType {}
+
+ /**
+ * Outgoing Diseqc message overflow.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_OVERFLOW =
+ Constants.LnbEventType.DISEQC_RX_OVERFLOW;
+ /**
+ * Outgoing Diseqc message isn't delivered on time.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_TIMEOUT =
+ Constants.LnbEventType.DISEQC_RX_TIMEOUT;
+ /**
+ * Incoming Diseqc message has parity error.
+ * @hide
+ */
+ public static final int EVENT_TYPE_DISEQC_RX_PARITY_ERROR =
+ Constants.LnbEventType.DISEQC_RX_PARITY_ERROR;
+ /**
+ * LNB is overload.
+ * @hide
+ */
+ public static final int EVENT_TYPE_LNB_OVERLOAD = Constants.LnbEventType.LNB_OVERLOAD;
+
int mId;
LnbCallback mCallback;
Context mContext;
diff --git a/media/java/android/media/tv/tuner/LnbCallback.java b/media/java/android/media/tv/tuner/LnbCallback.java
index 99bbf866aa69..5155f604b77b 100644
--- a/media/java/android/media/tv/tuner/LnbCallback.java
+++ b/media/java/android/media/tv/tuner/LnbCallback.java
@@ -17,6 +17,8 @@
package android.media.tv.tuner;
+import android.media.tv.tuner.Lnb.EventType;
+
/**
* Callback interface for receiving information from LNBs.
*
@@ -26,7 +28,7 @@ public interface LnbCallback {
/**
* Invoked when there is a LNB event.
*/
- void onEvent(int lnbEventType);
+ void onEvent(@EventType int lnbEventType);
/**
* Invoked when there is a new DiSEqC message.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 18969ae3279c..b76001facb45 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -24,18 +24,17 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.media.tv.tuner.TunerConstants.FilterStatus;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.TunerConstants.FrontendScanType;
import android.media.tv.tuner.TunerConstants.Result;
import android.media.tv.tuner.dvr.Dvr;
import android.media.tv.tuner.dvr.DvrCallback;
import android.media.tv.tuner.dvr.DvrSettings;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.filter.Filter.Subtype;
+import android.media.tv.tuner.filter.Filter.Type;
import android.media.tv.tuner.filter.FilterEvent;
import android.media.tv.tuner.filter.TimeFilter;
-import android.media.tv.tuner.frontend.FrontendCallback;
import android.media.tv.tuner.frontend.FrontendInfo;
import android.media.tv.tuner.frontend.FrontendStatus;
+import android.media.tv.tuner.frontend.OnTuneEventListener;
import android.media.tv.tuner.frontend.ScanCallback;
import android.os.Handler;
import android.os.Looper;
@@ -58,7 +57,6 @@ public final class Tuner implements AutoCloseable {
private static final String TAG = "MediaTvTuner";
private static final boolean DEBUG = false;
- private static final int MSG_ON_FRONTEND_EVENT = 1;
private static final int MSG_ON_FILTER_EVENT = 2;
private static final int MSG_ON_FILTER_STATUS = 3;
private static final int MSG_ON_LNB_EVENT = 4;
@@ -77,6 +75,10 @@ public final class Tuner implements AutoCloseable {
private List<Integer> mLnbIds;
private Lnb mLnb;
@Nullable
+ private OnTuneEventListener mOnTuneEventListener;
+ @Nullable
+ private Executor mOnTunerEventExecutor;
+ @Nullable
private ScanCallback mScanCallback;
@Nullable
private Executor mScanCallbackExecutor;
@@ -102,7 +104,8 @@ public final class Tuner implements AutoCloseable {
* TODO: replace the other constructor
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
- public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase) {
+ public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, int useCase,
+ @Nullable OnResourceLostListener listener) {
mContext = context;
}
@@ -113,7 +116,8 @@ public final class Tuner implements AutoCloseable {
*
* @hide
*/
- public void shareFrontend(@NonNull Tuner tuner) { }
+ public void shareFrontendFromTuner(@NonNull Tuner tuner) {
+ }
private long mNativeContext; // used by native jMediaTuner
@@ -221,11 +225,6 @@ public final class Tuner implements AutoCloseable {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_ON_FRONTEND_EVENT:
- if (mFrontend != null && mFrontend.mCallback != null) {
- mFrontend.mCallback.onEvent(msg.arg1);
- }
- break;
case MSG_ON_FILTER_STATUS: {
Filter filter = (Filter) msg.obj;
if (filter.mCallback != null) {
@@ -241,7 +240,6 @@ public final class Tuner implements AutoCloseable {
private class Frontend {
private int mId;
- private FrontendCallback mCallback;
private Frontend(int id) {
mId = id;
@@ -249,13 +247,59 @@ public final class Tuner implements AutoCloseable {
}
/**
- * Tunes the frontend to the settings given.
+ * Listens for tune events.
+ *
+ * <p>
+ * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link
+ * #stopTune()} is called.
+ *
+ * @param eventListener receives tune events.
+ * @throws SecurityException if the caller does not have appropriate permissions.
+ * @see #tune(FrontendSettings)
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void setOnTuneEventListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull OnTuneEventListener eventListener) {
+ TunerUtils.checkTunerPermission(mContext);
+ mOnTuneEventListener = eventListener;
+ mOnTunerEventExecutor = executor;
+ }
+
+ /**
+ * Clears the {@link OnTuneEventListener} and its associated {@link Executor}.
+ *
+ * @throws SecurityException if the caller does not have appropriate permissions.
+ * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ public void clearOnTuneEventListener() {
+ TunerUtils.checkTunerPermission(mContext);
+ mOnTuneEventListener = null;
+ mOnTunerEventExecutor = null;
+
+ }
+
+ /**
+ * Tunes the frontend to using the settings given.
+ *
+ * <p>
+ * This locks the frontend to a frequency by providing signal
+ * delivery information. If previous tuning isn't completed, this stop the previous tuning, and
+ * start a new tuning.
+ *
+ * <p>
+ * Tune is an async call, with {@link OnTuneEventListener#LOCKED LOCKED} and {@link
+ * OnTuneEventListener#NO_SIGNAL NO_SIGNAL} events sent to the {@link OnTuneEventListener}
+ * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}.
*
+ * @param settings Signal delivery information the frontend uses to
+ * search and lock the signal.
* @return result status of tune operation.
* @throws SecurityException if the caller does not have appropriate permissions.
- * TODO: add result constants or throw exceptions.
+ * @see #setOnTuneEventListener(Executor, OnTuneEventListener)
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Result
public int tune(@NonNull FrontendSettings settings) {
TunerUtils.checkTunerPermission(mContext);
return nativeTune(settings.getType(), settings);
@@ -268,8 +312,6 @@ public final class Tuner implements AutoCloseable {
* will be sent to attached filters.
*
* @return result status of the operation.
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Result
@@ -279,17 +321,26 @@ public final class Tuner implements AutoCloseable {
}
/**
- * Scan channels.
+ * Scan for channels.
+ *
+ * <p>Details for channels found are returned via {@link ScanCallback}.
*
* @param settings A {@link FrontendSettings} to configure the frontend.
* @param scanType The scan type.
- *
+ * @throws SecurityException if the caller does not have appropriate permissions.
+ * @throws IllegalStateException if {@code scan} is called again before {@link #stopScan()} is
+ * called.
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
- public int scan(@NonNull FrontendSettings settings, @FrontendScanType int scanType,
+ public int scan(@NonNull FrontendSettings settings, @ScanCallback.ScanType int scanType,
@NonNull @CallbackExecutor Executor executor, @NonNull ScanCallback scanCallback) {
TunerUtils.checkTunerPermission(mContext);
+ if (mScanCallback != null || mScanCallbackExecutor != null) {
+ throw new IllegalStateException(
+ "Scan already in progress. stopScan must be called before a new scan can be "
+ + "started.");
+ }
mScanCallback = scanCallback;
mScanCallbackExecutor = executor;
return nativeScan(settings.getType(), settings, scanType);
@@ -304,6 +355,7 @@ public final class Tuner implements AutoCloseable {
* <p>
* If the method completes successfully, the frontend stopped previous scanning.
*
+ * @throws SecurityException if the caller does not have appropriate permissions.
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@@ -490,8 +542,8 @@ public final class Tuner implements AutoCloseable {
}
private void onFrontendEvent(int eventType) {
- if (mHandler != null) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ON_FRONTEND_EVENT, eventType, 0));
+ if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
+ mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
}
}
@@ -513,18 +565,18 @@ public final class Tuner implements AutoCloseable {
* @param subType the subtype of the filter.
* @param bufferSize the buffer size of the filter to be opened in bytes. The buffer holds the
* data output from the filter.
- * @param cb the callback to receive notifications from filter.
* @param executor the executor on which callback will be invoked. The default event handler
* executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from filter.
* @return the opened filter. {@code null} if the operation failed.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public Filter openFilter(@FilterType int mainType, @FilterSubtype int subType,
- @BytesLong long bufferSize, @Nullable FilterCallback cb,
- @CallbackExecutor @Nullable Executor executor) {
+ public Filter openFilter(@Type int mainType, @Subtype int subType,
+ @BytesLong long bufferSize, @CallbackExecutor @Nullable Executor executor,
+ @Nullable FilterCallback cb) {
TunerUtils.checkTunerPermission(mContext);
Filter filter = nativeOpenFilter(
mainType, TunerUtils.getFilterSubtype(mainType, subType), bufferSize);
@@ -540,16 +592,34 @@ public final class Tuner implements AutoCloseable {
/**
* Opens an LNB (low-noise block downconverter) object.
*
+ * @param executor the executor on which callback will be invoked. The default event handler
+ * executor is used if it's {@code null}.
* @param cb the callback to receive notifications from LNB.
+ * @return the opened LNB object. {@code null} if the operation failed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+ @Nullable
+ public Lnb openLnb(@CallbackExecutor @Nullable Executor executor, LnbCallback cb) {
+ return openLnbByName(null, executor, cb);
+ }
+
+ /**
+ * Opens an LNB (low-noise block downconverter) object.
+ *
+ * @param name the LNB name.
* @param executor the executor on which callback will be invoked. The default event handler
* executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from LNB.
* @return the opened LNB object. {@code null} if the operation failed.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public Lnb openLnb(LnbCallback cb, @CallbackExecutor @Nullable Executor executor) {
+ public Lnb openLnbByName(@Nullable String name, @CallbackExecutor @Nullable Executor executor,
+ LnbCallback cb) {
TunerUtils.checkTunerPermission(mContext);
// TODO: use resource manager to get LNB ID.
return new Lnb(0);
@@ -608,17 +678,17 @@ public final class Tuner implements AutoCloseable {
* @param type the DVR type to be opened.
* @param bufferSize the buffer size of the output in bytes. It's used to hold output data of
* the attached filters.
- * @param cb the callback to receive notifications from DVR.
* @param executor the executor on which callback will be invoked. The default event handler
* executor is used if it's {@code null}.
+ * @param cb the callback to receive notifications from DVR.
* @return the opened DVR object. {@code null} if the operation failed.
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@Nullable
- public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize, DvrCallback cb,
- @CallbackExecutor @Nullable Executor executor) {
+ public Dvr openDvr(@DvrSettings.Type int type, @BytesLong long bufferSize,
+ @CallbackExecutor @Nullable Executor executor, DvrCallback cb) {
TunerUtils.checkTunerPermission(mContext);
Dvr dvr = nativeOpenDvr(type, bufferSize);
return dvr;
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index fa8f550867b8..20b77e663586 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -36,68 +36,17 @@ import java.lang.annotation.RetentionPolicy;
*/
@SystemApi
public final class TunerConstants {
- /** @hide */
+ /**
+ * Invalid TS packet ID.
+ * @hide
+ */
public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
- /** @hide */
+ /**
+ * Invalid stream ID.
+ * @hide
+ */
public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
-
- /** @hide */
- @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
- FRONTEND_EVENT_TYPE_LOST_LOCK})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendEventType {}
- /** @hide */
- public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
- /** @hide */
- public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
- /** @hide */
- public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
-
-
- /** @hide */
- @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
- FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
- FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
- FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP,
- FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, })
- @Retention(RetentionPolicy.SOURCE)
- public @interface FilterSubtype {}
- /** @hide */
- public static final int FILTER_SUBTYPE_UNDEFINED = 0;
- /** @hide */
- public static final int FILTER_SUBTYPE_SECTION = 1;
- /** @hide */
- public static final int FILTER_SUBTYPE_PES = 2;
- /** @hide */
- public static final int FILTER_SUBTYPE_AUDIO = 3;
- /** @hide */
- public static final int FILTER_SUBTYPE_VIDEO = 4;
- /** @hide */
- public static final int FILTER_SUBTYPE_DOWNLOAD = 5;
- /** @hide */
- public static final int FILTER_SUBTYPE_RECORD = 6;
- /** @hide */
- public static final int FILTER_SUBTYPE_TS = 7;
- /** @hide */
- public static final int FILTER_SUBTYPE_PCR = 8;
- /** @hide */
- public static final int FILTER_SUBTYPE_TEMI = 9;
- /** @hide */
- public static final int FILTER_SUBTYPE_MMPT = 10;
- /** @hide */
- public static final int FILTER_SUBTYPE_NTP = 11;
- /** @hide */
- public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12;
- /** @hide */
- public static final int FILTER_SUBTYPE_IP = 13;
- /** @hide */
- public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14;
- /** @hide */
- public static final int FILTER_SUBTYPE_TLV = 15;
- /** @hide */
- public static final int FILTER_SUBTYPE_PTP = 16;
-
/** @hide */
@IntDef(flag = true, prefix = "FILTER_STATUS_", value = {FILTER_STATUS_DATA_READY,
FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER, FILTER_STATUS_OVERFLOW})
@@ -245,20 +194,6 @@ public final class TunerConstants {
public static final int SC_HEVC_INDEX_SLICE_TRAIL_CRA =
Constants.DemuxScHevcIndex.SLICE_TRAIL_CRA;
-
- /** @hide */
- @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FrontendScanType {}
- /** @hide */
- public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
- /** @hide */
- public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO;
- /** @hide */
- public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
-
-
-
/** @hide */
@LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
@@ -502,23 +437,6 @@ public final class TunerConstants {
/** @hide */
- @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
- FILTER_SETTINGS_ALP})
- @Retention(RetentionPolicy.SOURCE)
- public @interface FilterSettingsType {}
- /** @hide */
- public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
- /** @hide */
- public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
- /** @hide */
- public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
- /** @hide */
- public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
- /** @hide */
- public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
-
-
- /** @hide */
@IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 8780b726e3b2..30aaa0271eb0 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -19,9 +19,7 @@ package android.media.tv.tuner;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.tv.tuner.V1_0.Constants;
-import android.media.tv.tuner.TunerConstants.FilterSubtype;
-import android.media.tv.tuner.filter.FilterConfiguration;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
+import android.media.tv.tuner.filter.Filter;
/**
* Utility class for tuner framework.
@@ -50,91 +48,91 @@ public final class TunerUtils {
* @param mainType filter main type.
* @param subtype filter subtype.
*/
- public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
- if (mainType == FilterConfiguration.FILTER_TYPE_TS) {
+ public static int getFilterSubtype(@Filter.Type int mainType, @Filter.Subtype int subtype) {
+ if (mainType == Filter.TYPE_TS) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxTsFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxTsFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PES:
+ case Filter.SUBTYPE_PES:
return Constants.DemuxTsFilterType.PES;
- case TunerConstants.FILTER_SUBTYPE_TS:
+ case Filter.SUBTYPE_TS:
return Constants.DemuxTsFilterType.TS;
- case TunerConstants.FILTER_SUBTYPE_AUDIO:
+ case Filter.SUBTYPE_AUDIO:
return Constants.DemuxTsFilterType.AUDIO;
- case TunerConstants.FILTER_SUBTYPE_VIDEO:
+ case Filter.SUBTYPE_VIDEO:
return Constants.DemuxTsFilterType.VIDEO;
- case TunerConstants.FILTER_SUBTYPE_PCR:
+ case Filter.SUBTYPE_PCR:
return Constants.DemuxTsFilterType.PCR;
- case TunerConstants.FILTER_SUBTYPE_RECORD:
+ case Filter.SUBTYPE_RECORD:
return Constants.DemuxTsFilterType.RECORD;
- case TunerConstants.FILTER_SUBTYPE_TEMI:
+ case Filter.SUBTYPE_TEMI:
return Constants.DemuxTsFilterType.TEMI;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_MMTP) {
+ } else if (mainType == Filter.TYPE_MMTP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxMmtpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxMmtpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PES:
+ case Filter.SUBTYPE_PES:
return Constants.DemuxMmtpFilterType.PES;
- case TunerConstants.FILTER_SUBTYPE_MMPT:
+ case Filter.SUBTYPE_MMTP:
return Constants.DemuxMmtpFilterType.MMTP;
- case TunerConstants.FILTER_SUBTYPE_AUDIO:
+ case Filter.SUBTYPE_AUDIO:
return Constants.DemuxMmtpFilterType.AUDIO;
- case TunerConstants.FILTER_SUBTYPE_VIDEO:
+ case Filter.SUBTYPE_VIDEO:
return Constants.DemuxMmtpFilterType.VIDEO;
- case TunerConstants.FILTER_SUBTYPE_RECORD:
+ case Filter.SUBTYPE_RECORD:
return Constants.DemuxMmtpFilterType.RECORD;
- case TunerConstants.FILTER_SUBTYPE_DOWNLOAD:
+ case Filter.SUBTYPE_DOWNLOAD:
return Constants.DemuxMmtpFilterType.DOWNLOAD;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_IP) {
+ } else if (mainType == Filter.TYPE_IP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxIpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxIpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_NTP:
+ case Filter.SUBTYPE_NTP:
return Constants.DemuxIpFilterType.NTP;
- case TunerConstants.FILTER_SUBTYPE_IP_PAYLOAD:
+ case Filter.SUBTYPE_IP_PAYLOAD:
return Constants.DemuxIpFilterType.IP_PAYLOAD;
- case TunerConstants.FILTER_SUBTYPE_IP:
+ case Filter.SUBTYPE_IP:
return Constants.DemuxIpFilterType.IP;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxIpFilterType.PAYLOAD_THROUGH;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_TLV) {
+ } else if (mainType == Filter.TYPE_TLV) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxTlvFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxTlvFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_TLV:
+ case Filter.SUBTYPE_TLV:
return Constants.DemuxTlvFilterType.TLV;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxTlvFilterType.PAYLOAD_THROUGH;
default:
break;
}
- } else if (mainType == FilterConfiguration.FILTER_TYPE_ALP) {
+ } else if (mainType == Filter.TYPE_ALP) {
switch (subtype) {
- case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
+ case Filter.SUBTYPE_UNDEFINED:
return Constants.DemuxAlpFilterType.UNDEFINED;
- case TunerConstants.FILTER_SUBTYPE_SECTION:
+ case Filter.SUBTYPE_SECTION:
return Constants.DemuxAlpFilterType.SECTION;
- case TunerConstants.FILTER_SUBTYPE_PTP:
+ case Filter.SUBTYPE_PTP:
return Constants.DemuxAlpFilterType.PTP;
- case TunerConstants.FILTER_SUBTYPE_PAYLOAD_THROUGH:
+ case Filter.SUBTYPE_PAYLOAD_THROUGH:
return Constants.DemuxAlpFilterType.PAYLOAD_THROUGH;
default:
break;
diff --git a/media/java/android/media/tv/tuner/dvr/Dvr.java b/media/java/android/media/tv/tuner/dvr/Dvr.java
index 95508d3b366d..a17773c08a63 100644
--- a/media/java/android/media/tv/tuner/dvr/Dvr.java
+++ b/media/java/android/media/tv/tuner/dvr/Dvr.java
@@ -17,11 +17,16 @@
package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner.Filter;
import android.media.tv.tuner.TunerConstants.Result;
import android.os.ParcelFileDescriptor;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Digital Video Record (DVR) interface provides record control on Demux's output buffer and
* playback control on Demux's input buffer.
@@ -29,6 +34,37 @@ import android.os.ParcelFileDescriptor;
* @hide
*/
public class Dvr {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "PLAYBACK_STATUS_",
+ value = {PLAYBACK_STATUS_EMPTY, PLAYBACK_STATUS_ALMOST_EMPTY,
+ PLAYBACK_STATUS_ALMOST_FULL, PLAYBACK_STATUS_FULL})
+ @interface PlaybackStatus {}
+
+ /**
+ * The space of the playback is empty.
+ */
+ public static final int PLAYBACK_STATUS_EMPTY = Constants.PlaybackStatus.SPACE_EMPTY;
+ /**
+ * The space of the playback is almost empty.
+ *
+ * <p> the threshold is set in {@link DvrSettings}.
+ */
+ public static final int PLAYBACK_STATUS_ALMOST_EMPTY =
+ Constants.PlaybackStatus.SPACE_ALMOST_EMPTY;
+ /**
+ * The space of the playback is almost full.
+ *
+ * <p> the threshold is set in {@link DvrSettings}.
+ */
+ public static final int PLAYBACK_STATUS_ALMOST_FULL =
+ Constants.PlaybackStatus.SPACE_ALMOST_FULL;
+ /**
+ * The space of the playback is full.
+ */
+ public static final int PLAYBACK_STATUS_FULL = Constants.PlaybackStatus.SPACE_FULL;
+
+
private long mNativeContext;
private DvrCallback mCallback;
diff --git a/media/java/android/media/tv/tuner/dvr/DvrCallback.java b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
index 3d140f0accac..ee0cfa7ddc2e 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrCallback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrCallback.java
@@ -16,6 +16,9 @@
package android.media.tv.tuner.dvr;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.dvr.Dvr.PlaybackStatus;
+
/**
* Callback interface for receiving information from DVR interfaces.
*
@@ -25,9 +28,9 @@ public interface DvrCallback {
/**
* Invoked when record status changed.
*/
- void onRecordStatusChanged(int status);
+ void onRecordStatusChanged(@FilterStatus int status);
/**
* Invoked when playback status changed.
*/
- void onPlaybackStatusChanged(int status);
+ void onPlaybackStatusChanged(@PlaybackStatus int status);
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrSettings.java b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
index 46efd7a33e90..49e875afc5ac 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrSettings.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrSettings.java
@@ -19,8 +19,11 @@ package android.media.tv.tuner.dvr;
import android.annotation.BytesLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants.FilterStatus;
+import android.media.tv.tuner.TunerUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -94,9 +97,13 @@ public class DvrSettings {
/**
* Creates a builder for {@link DvrSettings}.
+ *
+ * @param context the context of the caller.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder newBuilder() {
+ public static Builder builder(Context context) {
+ TunerUtils.checkTunerPermission(context);
return new Builder();
}
diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java
index 940b5ae44a74..93eaaa47d186 100644
--- a/media/java/android/media/tv/tuner/filter/AvSettings.java
+++ b/media/java/android/media/tv/tuner/filter/AvSettings.java
@@ -19,9 +19,7 @@ package android.media.tv.tuner.filter;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Filter Settings for a Video and Audio.
@@ -35,8 +33,8 @@ public class AvSettings extends Settings {
super(TunerUtils.getFilterSubtype(
mainType,
isAudio
- ? TunerConstants.FILTER_SUBTYPE_AUDIO
- : TunerConstants.FILTER_SUBTYPE_VIDEO));
+ ? Filter.SUBTYPE_AUDIO
+ : Filter.SUBTYPE_VIDEO));
mIsPassthrough = isPassthrough;
}
@@ -57,7 +55,7 @@ public class AvSettings extends Settings {
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
public static Builder builder(
- @NonNull Context context, @FilterType int mainType, boolean isAudio) {
+ @NonNull Context context, @Filter.Type int mainType, boolean isAudio) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType, isAudio);
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index e3e1df064238..fa7744a00e4f 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -19,9 +19,7 @@ package android.media.tv.tuner.filter;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Filter Settings for a Download.
@@ -31,7 +29,7 @@ public class DownloadSettings extends Settings {
private final int mDownloadId;
private DownloadSettings(int mainType, int downloadId) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_DOWNLOAD));
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
mDownloadId = downloadId;
}
@@ -50,7 +48,7 @@ public class DownloadSettings extends Settings {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 804c0c53982f..3f6154be6af6 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -17,9 +17,15 @@
package android.media.tv.tuner.filter;
import android.annotation.BytesLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.Tuner.FilterCallback;
+import android.media.tv.tuner.TunerConstants.Result;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Tuner data filter.
@@ -29,6 +35,128 @@ import android.media.tv.tuner.Tuner.FilterCallback;
* @hide
*/
public class Filter implements AutoCloseable {
+ /** @hide */
+ @IntDef(prefix = "TYPE_",
+ value = {TYPE_TS, TYPE_MMTP, TYPE_IP, TYPE_TLV, TYPE_ALP})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * TS filter type.
+ */
+ public static final int TYPE_TS = Constants.DemuxFilterMainType.TS;
+ /**
+ * MMTP filter type.
+ */
+ public static final int TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+ /**
+ * IP filter type.
+ */
+ public static final int TYPE_IP = Constants.DemuxFilterMainType.IP;
+ /**
+ * TLV filter type.
+ */
+ public static final int TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+ /**
+ * ALP filter type.
+ */
+ public static final int TYPE_ALP = Constants.DemuxFilterMainType.ALP;
+
+ /** @hide */
+ @IntDef(prefix = "SUBTYPE_",
+ value = {SUBTYPE_UNDEFINED, SUBTYPE_SECTION, SUBTYPE_PES, SUBTYPE_AUDIO, SUBTYPE_VIDEO,
+ SUBTYPE_DOWNLOAD, SUBTYPE_RECORD, SUBTYPE_TS, SUBTYPE_PCR, SUBTYPE_TEMI,
+ SUBTYPE_MMTP, SUBTYPE_NTP, SUBTYPE_IP_PAYLOAD, SUBTYPE_IP,
+ SUBTYPE_PAYLOAD_THROUGH, SUBTYPE_TLV, SUBTYPE_PTP, })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Subtype {}
+ /**
+ * Filter subtype undefined.
+ * @hide
+ */
+ public static final int SUBTYPE_UNDEFINED = 0;
+ /**
+ * Section filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_SECTION = 1;
+ /**
+ * PES filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PES = 2;
+ /**
+ * Audio filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_AUDIO = 3;
+ /**
+ * Video filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_VIDEO = 4;
+ /**
+ * Download filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_DOWNLOAD = 5;
+ /**
+ * Record filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_RECORD = 6;
+ /**
+ * TS filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TS = 7;
+ /**
+ * PCR filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PCR = 8;
+ /**
+ * TEMI filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TEMI = 9;
+ /**
+ * MMTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_MMTP = 10;
+ /**
+ * NTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_NTP = 11;
+ /**
+ * Payload filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_IP_PAYLOAD = 12;
+ /**
+ * IP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_IP = 13;
+ /**
+ * Payload through filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PAYLOAD_THROUGH = 14;
+ /**
+ * TLV filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_TLV = 15;
+ /**
+ * PTP filter subtype.
+ * @hide
+ */
+ public static final int SUBTYPE_PTP = 16;
+
+
private long mNativeContext;
private FilterCallback mCallback;
private final int mId;
@@ -56,6 +184,7 @@ public class Filter implements AutoCloseable {
* @param config the configuration of the filter.
* @return result status of the operation.
*/
+ @Result
public int configure(@NonNull FilterConfiguration config) {
int subType = -1;
Settings s = config.getSettings();
@@ -68,6 +197,7 @@ public class Filter implements AutoCloseable {
/**
* Gets the filter Id.
*/
+ @Result
public int getId() {
return nativeGetId();
}
@@ -84,6 +214,7 @@ public class Filter implements AutoCloseable {
* use demux as data source if the filter instance is NULL.
* @return result status of the operation.
*/
+ @Result
public int setDataSource(@Nullable Filter source) {
return nativeSetDataSource(source);
}
@@ -93,6 +224,7 @@ public class Filter implements AutoCloseable {
*
* @return result status of the operation.
*/
+ @Result
public int start() {
return nativeStartFilter();
}
@@ -103,6 +235,7 @@ public class Filter implements AutoCloseable {
*
* @return result status of the operation.
*/
+ @Result
public int stop() {
return nativeStopFilter();
}
@@ -112,6 +245,7 @@ public class Filter implements AutoCloseable {
*
* @return result status of the operation.
*/
+ @Result
public int flush() {
return nativeFlushFilter();
}
@@ -124,6 +258,7 @@ public class Filter implements AutoCloseable {
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
+ @Result
public int read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
size = Math.min(size, buffer.length - offset);
return nativeRead(buffer, offset, size);
diff --git a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
index 68c722f244d4..c901e2b59185 100644
--- a/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/FilterConfiguration.java
@@ -32,7 +32,10 @@ import java.lang.annotation.RetentionPolicy;
@SystemApi
public abstract class FilterConfiguration {
- /** @hide */
+ /**
+ * TODO: moved to Filter. Remove it here.
+ * @hide
+ */
@IntDef(prefix = "FILTER_TYPE_", value =
{FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index 37f94ae377ae..0b5c56ba4429 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -16,6 +16,7 @@
package android.media.tv.tuner.filter;
+import android.annotation.BytesLong;
import android.annotation.Nullable;
import android.media.tv.tuner.Tuner.Filter;
@@ -28,23 +29,27 @@ public class MediaEvent extends FilterEvent{
private final int mStreamId;
private final boolean mIsPtsPresent;
private final long mPts;
- private final int mDataLength;
+ private final long mDataLength;
+ private final long mOffset;
private final Object mLinearBuffer;
private final boolean mIsSecureMemory;
+ private final long mDataId;
private final int mMpuSequenceNumber;
private final boolean mIsPrivateData;
private final AudioDescriptor mExtraMetaData;
// This constructor is used by JNI code only
- private MediaEvent(int streamId, boolean isPtsPresent, long pts, int dataLength, Object buffer,
- boolean isSecureMemory, int mpuSequenceNumber, boolean isPrivateData,
- AudioDescriptor extraMetaData) {
+ private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
+ Object buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
+ boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
mDataLength = dataLength;
+ mOffset = offset;
mLinearBuffer = buffer;
mIsSecureMemory = isSecureMemory;
+ mDataId = dataId;
mMpuSequenceNumber = mpuSequenceNumber;
mIsPrivateData = isPrivateData;
mExtraMetaData = extraMetaData;
@@ -76,11 +81,20 @@ public class MediaEvent extends FilterEvent{
/**
* Gets data size in bytes of audio or video frame.
*/
- public int getDataLength() {
+ @BytesLong
+ public long getDataLength() {
return mDataLength;
}
/**
+ * The offset in the memory block which is shared among multiple Media Events.
+ */
+ @BytesLong
+ public long getOffset() {
+ return mOffset;
+ }
+
+ /**
* Gets a linear buffer associated to the memory where audio or video data stays.
* TODO: use LinearBuffer when it's ready.
*
@@ -101,6 +115,15 @@ public class MediaEvent extends FilterEvent{
}
/**
+ * Gets the ID which is used by HAL to provide additional information for AV data.
+ *
+ * <p>For secure audio, it's the audio handle used by Audio Track.
+ */
+ public long getAvDataId() {
+ return mDataId;
+ }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
public int getMpuSequenceNumber() {
diff --git a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
index 83246e51c8b6..248f23a1797b 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpFilterConfiguration.java
@@ -39,7 +39,7 @@ public class MmtpFilterConfiguration extends FilterConfiguration {
}
/**
- * Gets MMPT PID.
+ * Gets MMTP PID.
*
* <p>Packet ID is used to specify packets in MMTP.
*/
@@ -69,7 +69,7 @@ public class MmtpFilterConfiguration extends FilterConfiguration {
}
/**
- * Sets MMPT PID.
+ * Sets MMTP PID.
*/
@NonNull
public Builder setMmtpPid(int mmtpPid) {
diff --git a/media/java/android/media/tv/tuner/filter/PesSettings.java b/media/java/android/media/tv/tuner/filter/PesSettings.java
index bfa1f8c67d97..0f83597ff43f 100644
--- a/media/java/android/media/tv/tuner/filter/PesSettings.java
+++ b/media/java/android/media/tv/tuner/filter/PesSettings.java
@@ -20,9 +20,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Filter Settings for a PES Data.
@@ -34,8 +32,8 @@ public class PesSettings extends Settings {
private final int mStreamId;
private final boolean mIsRaw;
- private PesSettings(@FilterType int mainType, int streamId, boolean isRaw) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_PES));
+ private PesSettings(@Filter.Type int mainType, int streamId, boolean isRaw) {
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_PES));
mStreamId = streamId;
mIsRaw = isRaw;
}
@@ -65,7 +63,7 @@ public class PesSettings extends Settings {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index 483370935352..4e9d67f60db4 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -24,7 +24,6 @@ import android.hardware.tv.tuner.V1_0.Constants;
import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerConstants.ScIndexType;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -151,7 +150,7 @@ public class RecordSettings extends Settings {
private final int mScIndexMask;
private RecordSettings(int mainType, int tsIndexType, int scIndexType, int scIndexMask) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_RECORD));
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_RECORD));
mTsIndexMask = tsIndexType;
mScIndexType = scIndexType;
mScIndexMask = scIndexMask;
@@ -187,7 +186,7 @@ public class RecordSettings extends Settings {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettings.java b/media/java/android/media/tv/tuner/filter/SectionSettings.java
index 36e3d7cfee43..b8d0fad5e06c 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettings.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettings.java
@@ -16,7 +16,6 @@
package android.media.tv.tuner.filter;
-import android.media.tv.tuner.TunerConstants;
import android.media.tv.tuner.TunerUtils;
/**
@@ -26,6 +25,6 @@ import android.media.tv.tuner.TunerUtils;
public class SectionSettings extends Settings {
SectionSettings(int mainType) {
- super(TunerUtils.getFilterSubtype(mainType, TunerConstants.FILTER_SUBTYPE_SECTION));
+ super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_SECTION));
}
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
index 0fa982e3dd01..a2d42d8cfe50 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithSectionBits.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Bits Settings for Section Filters.
@@ -73,7 +72,7 @@ public class SectionSettingsWithSectionBits extends SectionSettings {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
index 6542b89478b2..0c9cd2bc9e56 100644
--- a/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
+++ b/media/java/android/media/tv/tuner/filter/SectionSettingsWithTableInfo.java
@@ -20,7 +20,6 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.media.tv.tuner.TunerUtils;
-import android.media.tv.tuner.filter.FilterConfiguration.FilterType;
/**
* Table information for Section Filter.
@@ -57,7 +56,7 @@ public class SectionSettingsWithTableInfo extends SectionSettings {
*/
@RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
@NonNull
- public static Builder builder(@NonNull Context context, @FilterType int mainType) {
+ public static Builder builder(@NonNull Context context, @Filter.Type int mainType) {
TunerUtils.checkTunerPermission(context);
return new Builder(mainType);
}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index 5b3bffc44741..44dbcc01b16e 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -112,31 +112,31 @@ public class DvbsFrontendSettings extends FrontendSettings {
public @interface Rolloff {}
/**
- * Roll Off undefined.
+ * Rolloff range undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendDvbsRolloff.UNDEFINED;
/**
- * Roll Off 0_35.
+ * Rolloff range 0,35.
*/
public static final int ROLLOFF_0_35 = Constants.FrontendDvbsRolloff.ROLLOFF_0_35;
/**
- * Roll Off 0_25.
+ * Rolloff range 0,25.
*/
public static final int ROLLOFF_0_25 = Constants.FrontendDvbsRolloff.ROLLOFF_0_25;
/**
- * Roll Off 0_2.
+ * Rolloff range 0,20.
*/
public static final int ROLLOFF_0_20 = Constants.FrontendDvbsRolloff.ROLLOFF_0_20;
/**
- * Roll Off 0_15.
+ * Rolloff range 0,15.
*/
public static final int ROLLOFF_0_15 = Constants.FrontendDvbsRolloff.ROLLOFF_0_15;
/**
- * Roll Off 0_1.
+ * Rolloff range 0,10.
*/
public static final int ROLLOFF_0_10 = Constants.FrontendDvbsRolloff.ROLLOFF_0_10;
/**
- * Roll Off 0_5.
+ * Rolloff range 0,5.
*/
public static final int ROLLOFF_0_5 = Constants.FrontendDvbsRolloff.ROLLOFF_0_5;
@@ -188,6 +188,25 @@ public class DvbsFrontendSettings extends FrontendSettings {
*/
public static final int STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+ /** @hide */
+ @IntDef(prefix = "VCM_MODE_",
+ value = {VCM_MODE_UNDEFINED, VCM_MODE_AUTO, VCM_MODE_MANUAL})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VcmMode {}
+
+ /**
+ * VCM mode undefined.
+ */
+ public static final int VCM_MODE_UNDEFINED = Constants.FrontendDvbsVcmMode.UNDEFINED;
+ /**
+ * Auto VCM mode.
+ */
+ public static final int VCM_MODE_AUTO = Constants.FrontendDvbsVcmMode.AUTO;
+ /**
+ * Manual VCM mode.
+ */
+ public static final int VCM_MODE_MANUAL = Constants.FrontendDvbsVcmMode.MANUAL;
+
private final int mModulation;
private final DvbsCodeRate mCoderate;
@@ -196,9 +215,10 @@ public class DvbsFrontendSettings extends FrontendSettings {
private final int mPilot;
private final int mInputStreamId;
private final int mStandard;
+ private final int mVcmMode;
private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate coderate,
- int symbolRate, int rolloff, int pilot, int inputStreamId, int standard) {
+ int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) {
super(frequency);
mModulation = modulation;
mCoderate = coderate;
@@ -207,6 +227,7 @@ public class DvbsFrontendSettings extends FrontendSettings {
mPilot = pilot;
mInputStreamId = inputStreamId;
mStandard = standard;
+ mVcmMode = vcm;
}
/**
@@ -256,6 +277,13 @@ public class DvbsFrontendSettings extends FrontendSettings {
public int getStandard() {
return mStandard;
}
+ /**
+ * Gets VCM mode.
+ */
+ @VcmMode
+ public int getVcmMode() {
+ return mVcmMode;
+ }
/**
* Creates a builder for {@link DvbsFrontendSettings}.
@@ -280,6 +308,7 @@ public class DvbsFrontendSettings extends FrontendSettings {
private int mPilot;
private int mInputStreamId;
private int mStandard;
+ private int mVcmMode;
private Builder() {
}
@@ -340,6 +369,14 @@ public class DvbsFrontendSettings extends FrontendSettings {
mStandard = standard;
return this;
}
+ /**
+ * Sets VCM mode.
+ */
+ @NonNull
+ public Builder setVcmMode(@VcmMode int vcm) {
+ mVcmMode = vcm;
+ return this;
+ }
/**
* Builds a {@link DvbsFrontendSettings} object.
@@ -347,7 +384,7 @@ public class DvbsFrontendSettings extends FrontendSettings {
@NonNull
public DvbsFrontendSettings build() {
return new DvbsFrontendSettings(mFrequency, mModulation, mCoderate, mSymbolRate,
- mRolloff, mPilot, mInputStreamId, mStandard);
+ mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode);
}
@Override
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index f0469b71f32c..9a82de0b6db3 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -110,7 +110,7 @@ public class DvbtFrontendSettings extends FrontendSettings {
*/
public static final int BANDWIDTH_5MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
/**
- * 1.7 MHz bandwidth.
+ * 1,7 MHz bandwidth.
*/
public static final int BANDWIDTH_1_7MHZ = Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
/**
@@ -232,39 +232,39 @@ public class DvbtFrontendSettings extends FrontendSettings {
*/
public static final int CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendDvbtCoderate.CODERATE_1_2;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendDvbtCoderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendDvbtCoderate.CODERATE_3_4;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendDvbtCoderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendDvbtCoderate.CODERATE_7_8;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_3_5 = Constants.FrontendDvbtCoderate.CODERATE_3_5;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_4_5 = Constants.FrontendDvbtCoderate.CODERATE_4_5;
/**
- * 6_7 code rate.
+ * 6/7 code rate.
*/
public static final int CODERATE_6_7 = Constants.FrontendDvbtCoderate.CODERATE_6_7;
/**
- * 8_9 code rate.
+ * 8/9 code rate.
*/
public static final int CODERATE_8_9 = Constants.FrontendDvbtCoderate.CODERATE_8_9;
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 7e6f1888cc4e..a83d771af1a2 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -87,47 +87,47 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
*/
public static final int CODERATE_AUTO = Constants.FrontendIsdbs3Coderate.AUTO;
/**
- * 1_3 code rate.
+ * 1/3 code rate.
*/
public static final int CODERATE_1_3 = Constants.FrontendIsdbs3Coderate.CODERATE_1_3;
/**
- * 2_5 code rate.
+ * 2/5 code rate.
*/
public static final int CODERATE_2_5 = Constants.FrontendIsdbs3Coderate.CODERATE_2_5;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendIsdbs3Coderate.CODERATE_1_2;
/**
- * 3_5 code rate.
+ * 3/5 code rate.
*/
public static final int CODERATE_3_5 = Constants.FrontendIsdbs3Coderate.CODERATE_3_5;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendIsdbs3Coderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendIsdbs3Coderate.CODERATE_3_4;
/**
- * 7_9 code rate.
+ * 7/9 code rate.
*/
public static final int CODERATE_7_9 = Constants.FrontendIsdbs3Coderate.CODERATE_7_9;
/**
- * 4_5 code rate.
+ * 4/5 code rate.
*/
public static final int CODERATE_4_5 = Constants.FrontendIsdbs3Coderate.CODERATE_4_5;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendIsdbs3Coderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendIsdbs3Coderate.CODERATE_7_8;
/**
- * 9_10 code rate.
+ * 9/10 code rate.
*/
public static final int CODERATE_9_10 = Constants.FrontendIsdbs3Coderate.CODERATE_9_10;
@@ -138,11 +138,11 @@ public class Isdbs3FrontendSettings extends FrontendSettings {
public @interface Rolloff {}
/**
- * Roll off type undefined.
+ * Rolloff type undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
/**
- * 0.03 roll off type.
+ * 0,03 Rolloff.
*/
public static final int ROLLOFF_0_03 = Constants.FrontendIsdbs3Rolloff.ROLLOFF_0_03;
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index fe100f8ecdd3..bb809bf8d455 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -96,23 +96,23 @@ public class IsdbsFrontendSettings extends FrontendSettings {
*/
public static final int CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
/**
- * 1_2 code rate.
+ * 1/2 code rate.
*/
public static final int CODERATE_1_2 = Constants.FrontendIsdbsCoderate.CODERATE_1_2;
/**
- * 2_3 code rate.
+ * 2/3 code rate.
*/
public static final int CODERATE_2_3 = Constants.FrontendIsdbsCoderate.CODERATE_2_3;
/**
- * 3_4 code rate.
+ * 3/4 code rate.
*/
public static final int CODERATE_3_4 = Constants.FrontendIsdbsCoderate.CODERATE_3_4;
/**
- * 5_6 code rate.
+ * 5/6 code rate.
*/
public static final int CODERATE_5_6 = Constants.FrontendIsdbsCoderate.CODERATE_5_6;
/**
- * 7_8 code rate.
+ * 7/8 code rate.
*/
public static final int CODERATE_7_8 = Constants.FrontendIsdbsCoderate.CODERATE_7_8;
@@ -123,11 +123,11 @@ public class IsdbsFrontendSettings extends FrontendSettings {
public @interface Rolloff {}
/**
- * Roll off type undefined.
+ * Rolloff type undefined.
*/
public static final int ROLLOFF_UNDEFINED = Constants.FrontendIsdbs3Rolloff.UNDEFINED;
/**
- * 0.35 roll off type.
+ * 0,35 rolloff.
*/
public static final int ROLLOFF_0_35 = Constants.FrontendIsdbsRolloff.ROLLOFF_0_35;
diff --git a/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
new file mode 100644
index 000000000000..5cf0d319c7c9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/OnTuneEventListener.java
@@ -0,0 +1,48 @@
+/*
+ * 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.tv.tuner.frontend;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Listens for tune events.
+ *
+ * @hide
+ */
+@SystemApi
+public interface OnTuneEventListener {
+
+ /** @hide */
+ @IntDef(prefix = "SIGNAL_", value = {SIGNAL_LOCKED, SIGNAL_NO_SIGNAL, SIGNAL_LOST_LOCK})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface TuneEvent {}
+
+ /** The frontend has locked to the signal specified by the tune method. */
+ int SIGNAL_LOCKED = Constants.FrontendEventType.LOCKED;
+ /** The frontend is unable to lock to the signal specified by the tune method. */
+ int SIGNAL_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+ /** The frontend has lost the lock to the signal specified by the tune method. */
+ int SIGNAL_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
+
+ /** Tune Event from the frontend */
+ void onTuneEvent(@TuneEvent int tuneEvent);
+}
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index 5e7d2189706c..a825d6d486ae 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -16,26 +16,55 @@
package android.media.tv.tuner.frontend;
+import android.annotation.IntDef;
+import android.hardware.tv.tuner.V1_0.Constants;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Scan callback.
*
* @hide
*/
public interface ScanCallback {
+
+ /** @hide */
+ @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ScanType {}
+ /**
+ * Scan type undefined.
+ */
+ int SCAN_TYPE_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+ /**
+ * Scan type auto.
+ *
+ * <p> Tuner will send {@link #onLocked}
+ */
+ int SCAN_TYPE_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+ /**
+ * Blind scan.
+ *
+ * <p>Frequency range is not specified. The {@link android.media.tv.tuner.Tuner} will scan an
+ * implementation specific range.
+ */
+ int SCAN_TYPE_BLIND = Constants.FrontendScanType.SCAN_BLIND;
+
/** Scan locked the signal. */
- void onLocked(boolean isLocked);
+ void onLocked();
/** Scan stopped. */
- void onEnd(boolean isEnd);
+ void onScanStopped();
/** scan progress percent (0..100) */
void onProgress(int percent);
- /** Signal frequency in Hertz */
- void onFrequencyReport(int frequency);
+ /** Signal frequencies in Hertz */
+ void onFrequenciesReport(int[] frequency);
/** Symbols per second */
- void onSymbolRate(int rate);
+ void onSymbolRates(int[] rate);
/** Locked Plp Ids for DVBT2 frontend. */
void onPlpIds(int[] plpIds);
@@ -46,15 +75,24 @@ public interface ScanCallback {
/** Stream Ids. */
void onInputStreamIds(int[] inputStreamIds);
- /** Locked signal standard. */
+ /** Locked signal standard for DVBS. */
void onDvbsStandard(@DvbsFrontendSettings.Standard int dvbsStandandard);
- /** Locked signal standard. */
+ /** Locked signal standard. for DVBT */
void onDvbtStandard(@DvbtFrontendSettings.Standard int dvbtStandard);
+ /** Locked signal SIF standard for Analog. */
+ void onAnalogSifStandard(@AnalogFrontendSettings.SifStandard int sif);
+
/** PLP status in a tuned frequency band for ATSC3 frontend. */
void onAtsc3PlpInfos(Atsc3PlpInfo[] atsc3PlpInfos);
+ /** Frontend hierarchy. */
+ void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy);
+
+ /** Frontend hierarchy. */
+ void onSignalType(@AnalogFrontendSettings.SignalType int signalType);
+
/** PLP information for ATSC3. */
class Atsc3PlpInfo {
private final int mPlpId;
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index cc2d1b193970..6595cae3c028 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -19,10 +19,12 @@ package com.android.mediarouteprovider.example;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import android.annotation.Nullable;
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderService;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
@@ -167,7 +169,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
@Override
- public void onCreateSession(String packageName, String routeId, long requestId) {
+ public void onCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle sessionHints) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
// Tell the router that session cannot be created by passing null as sessionInfo.
@@ -188,6 +191,8 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
.addSelectedRoute(routeId)
.addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
.addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+ // Set control hints with given sessionHints
+ .setControlHints(sessionHints)
.build();
notifySessionCreated(sessionInfo, requestId);
publishRoutes();
@@ -210,6 +215,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
}
}
notifySessionReleased(sessionId);
+ publishRoutes();
}
@Override
@@ -265,6 +271,27 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
@Override
public void onTransferToRoute(String sessionId, String routeId) {
RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+ MediaRoute2Info route = mRoutes.get(routeId);
+
+ if (sessionInfo == null || route == null) {
+ return;
+ }
+
+ for (String selectedRouteId : sessionInfo.getSelectedRoutes()) {
+ mRouteIdToSessionId.remove(selectedRouteId);
+ MediaRoute2Info selectedRoute = mRoutes.get(selectedRouteId);
+ if (selectedRoute != null) {
+ mRoutes.put(selectedRouteId, new MediaRoute2Info.Builder(selectedRoute)
+ .setClientPackageName(null)
+ .build());
+ }
+ }
+
+ mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+ .setClientPackageName(sessionInfo.getClientPackageName())
+ .build());
+ mRouteIdToSessionId.put(routeId, sessionId);
+
RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
.clearSelectedRoutes()
.addSelectedRoute(routeId)
@@ -272,6 +299,7 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService
.removeTransferrableRoute(routeId)
.build();
notifySessionUpdated(newSessionInfo);
+ publishRoutes();
}
void maybeDeselectRoute(String routeId) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index e782aae7c2d0..32d03db6f7ee 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -43,12 +43,14 @@ import android.annotation.NonNull;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
+import android.media.MediaRouter2.OnCreateSessionListener;
import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.RoutingController;
import android.media.MediaRouter2.SessionCallback;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -80,6 +82,9 @@ public class MediaRouter2Test {
private static final int TIMEOUT_MS = 5000;
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_VALUE = "test_value";
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -328,6 +333,74 @@ public class MediaRouter2Test {
}
@Test
+ public void testSetOnCreateSessionListener() throws Exception {
+ final List<String> sampleRouteFeature = new ArrayList<>();
+ sampleRouteFeature.add(FEATURE_SAMPLE);
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleRouteFeature);
+ MediaRoute2Info route = routes.get(ROUTE_ID1);
+ assertNotNull(route);
+
+ final Bundle createSessionHints = new Bundle();
+ createSessionHints.putString(TEST_KEY, TEST_VALUE);
+ final OnCreateSessionListener listener = new OnCreateSessionListener() {
+ @Override
+ public Bundle onCreateSession(MediaRoute2Info route) {
+ return createSessionHints;
+ }
+ };
+
+ final CountDownLatch successLatch = new CountDownLatch(1);
+ final CountDownLatch failureLatch = new CountDownLatch(1);
+ final List<RoutingController> controllers = new ArrayList<>();
+
+ // Create session with this route
+ SessionCallback sessionCallback = new SessionCallback() {
+ @Override
+ public void onSessionCreated(RoutingController controller) {
+ assertNotNull(controller);
+ assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
+
+ // The SampleMediaRoute2ProviderService supposed to set control hints
+ // with the given creationSessionHints.
+ Bundle controlHints = controller.getControlHints();
+ assertNotNull(controlHints);
+ assertTrue(controlHints.containsKey(TEST_KEY));
+ assertEquals(TEST_VALUE, controlHints.getString(TEST_KEY));
+
+ controllers.add(controller);
+ successLatch.countDown();
+ }
+
+ @Override
+ public void onSessionCreationFailed(MediaRoute2Info requestedRoute) {
+ failureLatch.countDown();
+ }
+ };
+
+ // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+ RouteCallback routeCallback = new RouteCallback();
+ mRouter2.registerRouteCallback(mExecutor, routeCallback, RouteDiscoveryPreference.EMPTY);
+
+ try {
+ mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+
+ // The SampleMediaRoute2ProviderService supposed to set control hints
+ // with the given creationSessionHints.
+ mRouter2.setOnCreateSessionListener(listener);
+ mRouter2.requestCreateSession(route);
+ assertTrue(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ // onSessionCreationFailed should not be called.
+ assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ releaseControllers(controllers);
+ mRouter2.unregisterRouteCallback(routeCallback);
+ mRouter2.unregisterSessionCallback(sessionCallback);
+ }
+ }
+
+ @Test
public void testSessionCallbackIsNotCalledAfterUnregistered() throws Exception {
final List<String> sampleRouteType = new ArrayList<>();
sampleRouteType.add(FEATURE_SAMPLE);
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 83dd0c0fd301..cba8452fe9c2 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -31,6 +31,7 @@ import android.media.MediaRouter2.RouteCallback;
import android.media.MediaRouter2.SessionCallback;
import android.media.MediaRouter2Manager;
import android.media.RouteDiscoveryPreference;
+import android.media.RoutingSessionInfo;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -38,7 +39,6 @@ import android.text.TextUtils;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -138,6 +138,8 @@ public class MediaRouterManagerTest {
@After
public void tearDown() {
+ // order matters (callbacks should be cleared at the last)
+ releaseAllSessions();
// unregister callbacks
clearCallbacks();
}
@@ -223,110 +225,83 @@ public class MediaRouterManagerTest {
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
assertNotNull(routeToSelect);
- try {
- mManager.selectRoute(mPackageName, routeToSelect);
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } finally {
- //TODO: release the session
- //mManager.selectRoute(mPackageName, null);
- }
- }
-
- /**
- * Tests if MR2Manager.Callback.onRouteSelected is called
- * when a route is selected by MR2Manager.
- */
- @Test
- @Ignore("TODO: test session created callback instead of onRouteSelected")
- public void testManagerOnRouteSelected() throws Exception {
- CountDownLatch latch = new CountDownLatch(1);
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
-
- addRouterCallback(new RouteCallback());
- addManagerCallback(new MediaRouter2Manager.Callback() {
- @Override
- public void onRouteSelected(String packageName, MediaRoute2Info route) {
- if (TextUtils.equals(mPackageName, packageName)
- && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
- latch.countDown();
- }
- }
- });
-
- MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
- assertNotNull(routeToSelect);
-
- try {
- mManager.selectRoute(mPackageName, routeToSelect);
- assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- } finally {
- //TODO: release the session
- //mManager.selectRoute(mPackageName, null);
- }
+ mManager.selectRoute(mPackageName, routeToSelect);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ assertEquals(1, mManager.getActiveSessions().size());
}
@Test
- @Ignore("TODO: enable this when 'releasing session' is implemented")
- public void testGetActiveRoutes() throws Exception {
+ public void testGetRoutingControllers() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
addRouterCallback(new RouteCallback());
addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
- public void onRouteSelected(String packageName, MediaRoute2Info route) {
- if (TextUtils.equals(mPackageName, packageName)
- && route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
+ public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+ if (TextUtils.equals(mPackageName, controller.getClientPackageName())
+ && createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) {
latch.countDown();
}
}
});
- //TODO: it fails due to not releasing session
- assertEquals(0, mManager.getActiveSessions().size());
+ assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- assertEquals(1, mManager.getActiveSessions().size());
+ List<MediaRouter2Manager.RoutingController> controllers =
+ mManager.getRoutingControllers(mPackageName);
- //TODO: release the session
- /*
+ assertEquals(1, controllers.size());
+
+ MediaRouter2Manager.RoutingController routingController = controllers.get(0);
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, null),
+ () -> routingController.release(),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), null));
- assertEquals(0, mManager.getActiveRoutes().size());
- */
+ assertEquals(0, mManager.getRoutingControllers(mPackageName).size());
}
/**
- * Tests selecting and unselecting routes of a single provider.
+ * Tests select, transfer, release of routes of a provider
*/
@Test
- @Ignore("TODO: enable when session is released")
- public void testSingleProviderSelect() throws Exception {
+ public void testSelectAndTransferAndRelease() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
addRouterCallback(new RouteCallback());
+ CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+
+ addManagerCallback(new MediaRouter2Manager.Callback() {
+ @Override
+ public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+ assertNotNull(controller);
+ onSessionCreatedLatch.countDown();
+ }
+ });
awaitOnRouteChangedManager(
() -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
+ assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+ List<MediaRouter2Manager.RoutingController> controllers =
+ mManager.getRoutingControllers(mPackageName);
+
+ assertEquals(1, controllers.size());
+ MediaRouter2Manager.RoutingController routingController = controllers.get(0);
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID2)),
- ROUTE_ID2,
+ () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
+ ROUTE_ID5_TO_TRANSFER_TO,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
- //TODO: release the session
- /*
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, null),
- ROUTE_ID2,
+ () -> routingController.release(),
+ ROUTE_ID5_TO_TRANSFER_TO,
route -> TextUtils.equals(route.getClientPackageName(), null));
-
- */
}
@Test
@@ -460,4 +435,13 @@ public class MediaRouterManagerTest {
}
mSessionCallbacks.clear();
}
+
+ private void releaseAllSessions() {
+ // ensure ManagerRecord in MediaRouter2ServiceImpl
+ addManagerCallback(new MediaRouter2Manager.Callback());
+
+ for (RoutingSessionInfo session : mManager.getActiveSessions()) {
+ mManager.getControllerForSession(session).release();
+ }
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 6ea2c741cc35..a337570829e4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -195,6 +195,7 @@ public class SettingsBackupTest {
Settings.Global.CERT_PIN_UPDATE_CONTENT_URL,
Settings.Global.CERT_PIN_UPDATE_METADATA_URL,
Settings.Global.COMPATIBILITY_MODE,
+ Settings.Global.COMMON_CRITERIA_MODE,
Settings.Global.CONNECTIVITY_CHANGE_DELAY,
Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE,
Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index f04226e7ceaf..cd90efe9a215 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -32,8 +32,8 @@
android:orientation="horizontal">
<FrameLayout
- android:layout_width="90dp"
- android:layout_height="94dp">
+ android:layout_width="45dp"
+ android:layout_height="47dp">
<View
android:id="@+id/icon_container_bg"
@@ -43,35 +43,34 @@
<FrameLayout
android:id="@+id/icon_mic"
- android:layout_width="70dp"
- android:layout_height="70dp"
- android:layout_marginLeft="12dp"
- android:layout_marginTop="12dp"
- android:layout_marginRight="8dp"
- android:layout_marginBottom="12dp">
+ android:layout_width="35dp"
+ android:layout_height="35dp"
+ android:layout_marginLeft="6dp"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp">
<View
- android:layout_width="54dp"
- android:layout_height="54dp"
+ android:layout_width="27dp"
+ android:layout_height="27dp"
android:layout_gravity="center"
android:background="@drawable/tv_circle_dark"/>
<ImageView
android:id="@+id/pulsating_circle"
- android:layout_width="54dp"
- android:layout_height="54dp"
+ android:layout_width="27dp"
+ android:layout_height="27dp"
android:layout_gravity="center"
android:background="@drawable/tv_circle_white_translucent"/>
<ImageView
- android:layout_width="54dp"
- android:layout_height="54dp"
+ android:layout_width="27dp"
+ android:layout_height="27dp"
android:layout_gravity="center"
android:src="@drawable/tv_ring_white"/>
<ImageView
- android:layout_width="32dp"
- android:layout_height="32dp"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
android:layout_gravity="center"
android:background="@drawable/tv_ic_mic_white"/>
</FrameLayout>
@@ -81,29 +80,30 @@
<LinearLayout
android:id="@+id/texts_container"
android:layout_width="wrap_content"
- android:layout_height="94dp"
+ android:layout_height="47dp"
android:background="@color/tv_audio_recording_indicator_background"
- android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="visible">
<TextView
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="14dp"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="1dp"
android:text="@string/mic_active"
android:textColor="@android:color/white"
android:fontFamily="sans-serif"
- android:textSize="20dp"/>
+ android:textSize="10dp"/>
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="14dp"
android:singleLine="true"
android:text="SomeApplication accessed your microphone"
android:textColor="@android:color/white"
android:fontFamily="sans-serif"
- android:textSize="16dp"/>
+ android:textSize="8dp"/>
</LinearLayout>
@@ -113,8 +113,8 @@
<View
android:id="@+id/bg_right"
- android:layout_width="24dp"
- android:layout_height="94dp"
+ android:layout_width="12dp"
+ android:layout_height="47dp"
android:background="@drawable/tv_rect_dark_right_rounded"
android:visibility="visible"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 896315725644..e52010600ab3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -484,4 +484,13 @@
<!-- Package name for controls plugin -->
<string name="config_controlsPluginPackageName" translatable="false">com.android.systemui.controls.panel</string>
+ <!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that
+ are part of the blacklist are never displayed. Each item in the blacklist must be a string
+ defined in core/res/res/config.xml to properly blacklist the icon.
+ -->
+ <string-array name="config_statusBarIconBlackList" translatable="false">
+ <item>@*android:string/status_bar_rotate</item>
+ <item>@*android:string/status_bar_headset</item>
+ </string-array>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d68fe15844c1..a46ab3a9e35b 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -288,7 +288,8 @@ public class BatteryMeterView extends LinearLayout implements
@Override
public void onTuningChanged(String key, String newValue) {
if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- ArraySet<String> icons = StatusBarIconController.getIconBlacklist(newValue);
+ ArraySet<String> icons = StatusBarIconController.getIconBlacklist(
+ getContext(), newValue);
setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 895207d37816..898cd1363d2b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -17,6 +17,8 @@
package com.android.systemui.accessibility;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
@@ -35,10 +37,25 @@ public class WindowMagnification extends SystemUI {
private WindowMagnificationController mWindowMagnificationController;
private final Handler mHandler;
+ private Configuration mLastConfiguration;
+
@Inject
public WindowMagnification(Context context, @Main Handler mainHandler) {
super(context);
mHandler = mainHandler;
+ mLastConfiguration = new Configuration(context.getResources().getConfiguration());
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ final int configDiff = newConfig.diff(mLastConfiguration);
+ if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) {
+ return;
+ }
+ mLastConfiguration.setTo(newConfig);
+ if (mWindowMagnificationController != null) {
+ mWindowMagnificationController.onConfigurationChanged(configDiff);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index bfac4fce92eb..c243309d960a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -113,6 +113,7 @@ public class WindowMagnificationController implements View.OnClickListener,
if (mMirrorView != null) {
return;
}
+ setInitialStartBounds();
createOverlayWindow();
}
@@ -178,9 +179,20 @@ public class WindowMagnificationController implements View.OnClickListener,
}
}
- private void createMirrorWindow() {
- setInitialStartBounds();
+ /**
+ * Called when the configuration has changed, and it updates window magnification UI.
+ *
+ * @param configDiff a bit mask of the differences between the configurations
+ */
+ void onConfigurationChanged(int configDiff) {
+ // TODO(b/145780606): update toggle button UI.
+ if (mMirrorView != null) {
+ mWm.removeView(mMirrorView);
+ createMirrorWindow();
+ }
+ }
+ private void createMirrorWindow() {
// The window should be the size the mirrored surface will be but also add room for the
// border and the drag handle.
int dragViewHeight = (int) mContext.getResources().getDimension(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 86ed274e9b5c..f71150fc348c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -366,7 +366,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
- final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
+
if (TextUtils.isEmpty(tileList)) {
tileList = res.getString(R.string.quick_settings_tiles);
if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
@@ -380,11 +380,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
if (tile.isEmpty()) continue;
if (tile.equals("default")) {
if (!addedDefault) {
- tiles.addAll(Arrays.asList(defaultTileList.split(",")));
- if (Build.IS_DEBUGGABLE
- && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
- tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
- }
+ tiles.addAll(getDefaultSpecs(context));
addedDefault = true;
}
} else {
@@ -394,6 +390,28 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
return tiles;
}
+ /**
+ * Returns the default QS tiles for the context.
+ * @param context the context to obtain the resources from
+ * @return a list of specs of the default tiles
+ */
+ public static List<String> getDefaultSpecs(Context context) {
+ final ArrayList<String> tiles = new ArrayList<String>();
+
+ final Resources res = context.getResources();
+ final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
+ final String extraTileList = res.getString(
+ com.android.internal.R.string.config_defaultExtraQuickSettingsTiles);
+
+ tiles.addAll(Arrays.asList(defaultTileList.split(",")));
+ tiles.addAll(Arrays.asList(extraTileList.split(",")));
+ if (Build.IS_DEBUGGABLE
+ && GarbageMonitor.MemoryTile.ADD_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
+ tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
+ }
+ return tiles;
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("QSTileHost:");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 1de63550a1be..fb89ed264628 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -266,12 +266,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
}
private void reset() {
- ArrayList<String> tiles = new ArrayList<>();
- String defTiles = mContext.getString(R.string.quick_settings_tiles_default);
- for (String tile : defTiles.split(",")) {
- tiles.add(tile);
- }
- mTileAdapter.resetTileSpecs(mHost, tiles);
+ mTileAdapter.resetTileSpecs(mHost, QSTileHost.getDefaultSpecs(mContext));
}
private void setTileSpecs() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 3b106cbe10d1..64bdd9799ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.notification.row;
import static android.app.Notification.EXTRA_IS_GROUP_CONVERSATION;
-import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -54,6 +53,7 @@ import android.os.Handler;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -102,7 +102,6 @@ public class NotificationConversationInfo extends LinearLayout implements
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private boolean mIsDeviceProvisioned;
-
private int mStartingChannelImportance;
private boolean mStartedAsBubble;
private boolean mIsBubbleable;
@@ -127,6 +126,8 @@ public class NotificationConversationInfo extends LinearLayout implements
mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
} else {
mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
+ Settings.Global.putInt(
+ mContext.getContentResolver(), Settings.Global.NOTIFICATION_BUBBLES, 1);
}
closeControls(v, true);
};
@@ -209,11 +210,10 @@ public class NotificationConversationInfo extends LinearLayout implements
mLauncherApps = launcherApps;
mConversationId = mNotificationChannel.getConversationId();
if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
- mConversationId = mSbn.getNotification().getShortcutId();
+ mConversationId = mSbn.getShortcutId(mContext);
}
- // TODO: flag this when flag exists
if (TextUtils.isEmpty(mConversationId)) {
- mConversationId = mSbn.getId() + mSbn.getTag() + PLACEHOLDER_CONVERSATION_ID;
+ throw new IllegalArgumentException("Does not have required information");
}
// TODO: consider querying this earlier in the notification pipeline and passing it in
LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
@@ -240,10 +240,9 @@ public class NotificationConversationInfo extends LinearLayout implements
// a custom channel
if (TextUtils.isEmpty(mNotificationChannel.getConversationId())) {
try {
- // TODO: associate this key with this channel service side so the customization
- // isn't forgotten on the next update
mINotificationManager.createConversationNotificationChannelForPackage(
- mPackageName, mAppUid, mNotificationChannel, mConversationId);
+ mPackageName, mAppUid, mSbn.getKey(), mNotificationChannel,
+ mConversationId);
mNotificationChannel = mINotificationManager.getConversationNotificationChannel(
mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName,
mNotificationChannel.getId(), false, mConversationId);
@@ -360,7 +359,6 @@ public class NotificationConversationInfo extends LinearLayout implements
image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo,
mContext.getResources().getDisplayMetrics().densityDpi));
} else {
- // TODO: flag this behavior
if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
// TODO: maybe use a generic group icon, or a composite of recent senders
image.setImageDrawable(mPm.getDefaultActivityIcon());
@@ -388,7 +386,6 @@ public class NotificationConversationInfo extends LinearLayout implements
if (mShortcutInfo != null) {
name.setText(mShortcutInfo.getShortLabel());
} else {
- // TODO: flag this behavior
Bundle extras = mSbn.getNotification().extras;
String nameString = extras.getString(Notification.EXTRA_CONVERSATION_TITLE);
if (TextUtils.isEmpty(nameString)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index a9a4804a2be4..6b4511d31669 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -65,8 +65,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubbleExperimentConfig;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
@@ -102,7 +100,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
// standard controls
private static final int ACTION_ALERT = 5;
- private TextView mBubbleDescriptionView;
private TextView mPriorityDescriptionView;
private TextView mSilentDescriptionView;
@@ -120,7 +117,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private Set<NotificationChannel> mUniqueChannelsInRow;
private NotificationChannel mSingleNotificationChannel;
private int mStartingChannelImportance;
- private boolean mStartedAsBubble;
private boolean mWasShownHighPriority;
private boolean mPressedApply;
private boolean mPresentingChannelEditorDialog = false;
@@ -130,14 +126,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
* level; non-null once the user takes an action which indicates an explicit preference.
*/
@Nullable private Integer mChosenImportance;
- /**
- * The last bubble setting chosen by the user. Null if the user has not chosen a bubble level;
- * non-null once the user takes an action which indicates an explicit preference.
- */
- @Nullable private Boolean mChosenBubbleEnabled;
private boolean mIsSingleDefaultChannel;
private boolean mIsNonblockable;
- private boolean mIsBubbleable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private AnimatorSet mExpandAnimation;
@@ -149,8 +139,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private NotificationGuts mGutsContainer;
private Drawable mPkgIcon;
- private BubbleController mBubbleController;
-
/** Whether this view is being shown as part of the blocking helper. */
private boolean mIsForBlockingHelper;
@@ -167,9 +155,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private OnClickListener mOnAlert = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
mChosenImportance = IMPORTANCE_DEFAULT;
- if (mStartedAsBubble) {
- mChosenBubbleEnabled = false;
- }
applyAlertingBehavior(BEHAVIOR_ALERTING, true /* userTriggered */);
};
@@ -177,18 +162,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private OnClickListener mOnSilent = v -> {
mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
mChosenImportance = IMPORTANCE_LOW;
- if (mStartedAsBubble) {
- mChosenBubbleEnabled = false;
- }
applyAlertingBehavior(BEHAVIOR_SILENT, true /* userTriggered */);
};
- /** Used by standard ui (in an experiment) {@see BubbleExperimentConfig#allowNotifBubbleMenu} */
- private OnClickListener mOnBubble = v -> {
- mExitReason = NotificationCounters.BLOCKING_HELPER_KEEP_SHOWING;
- mChosenBubbleEnabled = true;
- applyAlertingBehavior(BEHAVIOR_BUBBLE, true /* userTriggered */);
- };
// used by standard ui
private OnClickListener mOnDismissSettings = v -> {
@@ -255,7 +231,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
protected void onFinishInflate() {
super.onFinishInflate();
- mBubbleDescriptionView = findViewById(R.id.bubble_summary);
mPriorityDescriptionView = findViewById(R.id.alert_summary);
mSilentDescriptionView = findViewById(R.id.silence_summary);
}
@@ -320,7 +295,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mMetricsLogger = Dependency.get(MetricsLogger.class);
mVisualStabilityManager = visualStabilityManager;
mChannelEditorDialogController = Dependency.get(ChannelEditorDialogController.class);
- mBubbleController = Dependency.get(BubbleController.class);
mPackageName = pkg;
mUniqueChannelsInRow = uniqueChannelsInRow;
mNumUniqueChannelsInRow = uniqueChannelsInRow.size();
@@ -352,9 +326,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
&& numTotalChannels == 1;
}
- mIsBubbleable = mEntry.getBubbleMetadata() != null;
- mStartedAsBubble = mEntry.isBubble();
-
bindHeader();
bindChannelDetails();
@@ -402,7 +373,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
findViewById(R.id.non_configurable_text).setVisibility(GONE);
findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE);
findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE);
- findViewById(R.id.bubble).setVisibility(mIsBubbleable ? VISIBLE : GONE);
}
View turnOffButton = findViewById(R.id.turn_off_notifications);
@@ -416,14 +386,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
View silent = findViewById(R.id.silence);
View alert = findViewById(R.id.alert);
- View bubble = findViewById(R.id.bubble);
silent.setOnClickListener(mOnSilent);
alert.setOnClickListener(mOnAlert);
- bubble.setOnClickListener(mOnBubble);
- int behavior = mStartedAsBubble
- ? BEHAVIOR_BUBBLE
- : mWasShownHighPriority
+ int behavior = mWasShownHighPriority
? BEHAVIOR_ALERTING
: BEHAVIOR_SILENT;
applyAlertingBehavior(behavior, false /* userTriggered */);
@@ -587,14 +553,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
}
- if (mChosenBubbleEnabled != null && mStartedAsBubble != mChosenBubbleEnabled) {
- if (mChosenBubbleEnabled) {
- mBubbleController.onUserCreatedBubbleFromNotification(mEntry);
- } else {
- mBubbleController.onUserDemotedBubbleFromNotification(mEntry);
- }
- }
-
Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
bgHandler.post(
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
@@ -630,7 +588,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
TransitionManager.beginDelayedTransition(this, transition);
}
- View bubble = findViewById(R.id.bubble);
View alert = findViewById(R.id.alert);
View silence = findViewById(R.id.silence);
@@ -638,33 +595,18 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
case BEHAVIOR_ALERTING:
mPriorityDescriptionView.setVisibility(VISIBLE);
mSilentDescriptionView.setVisibility(GONE);
- mBubbleDescriptionView.setVisibility(GONE);
post(() -> {
alert.setSelected(true);
silence.setSelected(false);
- bubble.setSelected(false);
});
break;
case BEHAVIOR_SILENT:
mSilentDescriptionView.setVisibility(VISIBLE);
mPriorityDescriptionView.setVisibility(GONE);
- mBubbleDescriptionView.setVisibility(GONE);
post(() -> {
alert.setSelected(false);
silence.setSelected(true);
- bubble.setSelected(false);
- });
- break;
-
- case BEHAVIOR_BUBBLE:
- mBubbleDescriptionView.setVisibility(VISIBLE);
- mSilentDescriptionView.setVisibility(GONE);
- mPriorityDescriptionView.setVisibility(GONE);
- post(() -> {
- alert.setSelected(false);
- silence.setSelected(false);
- bubble.setSelected(true);
});
break;
@@ -673,9 +615,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
boolean isAChange = mWasShownHighPriority != (behavior == BEHAVIOR_ALERTING);
- boolean isABubbleChange = mStartedAsBubble != (behavior == BEHAVIOR_BUBBLE);
TextView done = findViewById(R.id.done);
- done.setText((isAChange || isABubbleChange)
+ done.setText(isAChange
? R.string.inline_ok_button
: R.string.inline_done_button);
}
@@ -684,7 +625,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
switch (action) {
case ACTION_UNDO:
mChosenImportance = mStartingChannelImportance;
- mChosenBubbleEnabled = mStartedAsBubble;
break;
case ACTION_DELIVER_SILENTLY:
mExitReason = NotificationCounters.BLOCKING_HELPER_DELIVER_SILENTLY;
@@ -767,9 +707,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
if (mChosenImportance != null) {
mStartingChannelImportance = mChosenImportance;
}
- if (mChosenBubbleEnabled != null) {
- mStartedAsBubble = mChosenBubbleEnabled;
- }
mExitReason = NotificationCounters.BLOCKING_HELPER_DISMISSED;
if (mIsForBlockingHelper) {
@@ -969,9 +906,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
@Retention(SOURCE)
- @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE})
+ @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT})
private @interface AlertingBehavior {}
private static final int BEHAVIOR_ALERTING = 0;
private static final int BEHAVIOR_SILENT = 1;
- private static final int BEHAVIOR_BUBBLE = 2;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 5daef24cbad2..75da5d123972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -76,12 +76,12 @@ public interface StatusBarIconController {
public static final String ICON_BLACKLIST = "icon_blacklist";
- public static ArraySet<String> getIconBlacklist(String blackListStr) {
+ /** Reads the default blacklist from config value unless blacklistStr is provided. */
+ static ArraySet<String> getIconBlacklist(Context context, String blackListStr) {
ArraySet<String> ret = new ArraySet<>();
- if (blackListStr == null) {
- blackListStr = "rotate,headset";
- }
- String[] blacklist = blackListStr.split(",");
+ String[] blacklist = blackListStr == null
+ ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
+ : blackListStr.split(",");
for (String slot : blacklist) {
if (!TextUtils.isEmpty(slot)) {
ret.add(slot);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index aa062eb2e051..bfcbceaef9af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -111,7 +111,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu
return;
}
mIconBlacklist.clear();
- mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue));
+ mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue));
ArrayList<Slot> currentSlots = getSlots();
ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 8286d26e9999..d2e92629ed78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -117,7 +117,7 @@ public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallba
if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
return;
}
- ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
+ ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue);
boolean blockAirplane = blockList.contains(mSlotAirplane);
boolean blockMobile = blockList.contains(mSlotMobile);
boolean blockWifi = blockList.contains(mSlotWifi);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 4f0af9e166c9..759bad4f77b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -298,7 +298,7 @@ public class Clock extends TextView implements DemoMode, Tunable, CommandQueue.C
mShowSeconds = TunerService.parseIntegerSwitch(newValue, false);
updateShowSeconds();
} else {
- setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(newValue)
+ setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue)
.contains("clock"));
updateClockVisibility();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 905b9a398b68..66372c311325 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -64,7 +64,7 @@ public class BatteryPreference extends DropDownPreference implements TunerServic
@Override
public void onTuningChanged(String key, String newValue) {
if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+ mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
mBatteryEnabled = !mBlacklist.contains(mBattery);
}
if (!mHasSetValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index a526603372bd..f7d0c9fb9d86 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -61,7 +61,7 @@ public class ClockPreference extends DropDownPreference implements TunerService.
public void onTuningChanged(String key, String newValue) {
if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
mReceivedClock = true;
- mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+ mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
mClockEnabled = !mBlacklist.contains(mClock);
} else if (Clock.CLOCK_SECONDS.equals(key)) {
mReceivedSeconds = true;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
index 6f23e207c048..de8ccfa848e3 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
@@ -57,7 +57,7 @@ public class StatusBarSwitch extends SwitchPreference implements Tunable {
if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
return;
}
- mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
+ mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
setChecked(!mBlacklist.contains(getKey()));
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 19f0ba24b0fd..142fdc21aff1 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -117,7 +117,7 @@ public class TunerServiceImpl extends TunerService {
String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
if (blacklistStr != null) {
ArraySet<String> iconBlacklist =
- StatusBarIconController.getIconBlacklist(blacklistStr);
+ StatusBarIconController.getIconBlacklist(mContext, blacklistStr);
iconBlacklist.add("rotate");
iconBlacklist.add("headset");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index fad7cbd04c74..34111e2cba9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -44,6 +44,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.shared.plugins.PluginManager;
@@ -73,6 +74,7 @@ import javax.inject.Provider;
public class QSTileHostTest extends SysuiTestCase {
private static String MOCK_STATE_STRING = "MockState";
+ private static final String CUSTOM_TILE_SPEC = "custom(TEST_PKG/.TEST_CLS)";
@Mock
private StatusBarIconController mIconController;
@@ -92,6 +94,8 @@ public class QSTileHostTest extends SysuiTestCase {
private QSTile.State mMockState;
@Mock
private StatusBar mStatusBar;
+ @Mock
+ private CustomTile mCustomTile;
private Handler mHandler;
private TestableLooper mLooper;
@@ -103,9 +107,8 @@ public class QSTileHostTest extends SysuiTestCase {
mLooper = TestableLooper.get(this);
mHandler = new Handler(mLooper.getLooper());
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
- mLooper.getLooper(),
- mPluginManager, mTunerService, mAutoTiles, mDumpController, mBroadcastDispatcher,
- mStatusBar);
+ mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpController,
+ mBroadcastDispatcher, mStatusBar);
setUpTileFactory();
Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
"", ActivityManager.getCurrentUser());
@@ -121,10 +124,13 @@ public class QSTileHostTest extends SysuiTestCase {
return new TestTile1(mQSTileHost);
case "spec2":
return new TestTile2(mQSTileHost);
+ case CUSTOM_TILE_SPEC:
+ return mCustomTile;
default:
return null;
}
});
+ when(mCustomTile.isAvailable()).thenReturn(true);
}
@Test
@@ -173,6 +179,39 @@ public class QSTileHostTest extends SysuiTestCase {
assertEquals(output, w.getBuffer().toString());
}
+ @Test
+ public void testDefault() {
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.string.quick_settings_tiles_default, "spec1");
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+ assertEquals(1, mQSTileHost.getTiles().size());
+ QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
+ assertTrue(element instanceof TestTile1);
+ }
+
+ @Test
+ public void testDefaultAndExtra() {
+ mContext.getOrCreateTestableResources()
+ .addOverride(R.string.quick_settings_tiles_default, "spec1");
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultExtraQuickSettingsTiles, "spec2");
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+ assertEquals(2, mQSTileHost.getTiles().size());
+ QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]);
+ assertTrue(elements[0] instanceof TestTile1);
+ assertTrue(elements[1] instanceof TestTile2);
+ }
+
+ @Test
+ public void testExtraCustom() {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
+ CUSTOM_TILE_SPEC);
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
+ assertEquals(1, mQSTileHost.getTiles().size());
+ assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles()));
+ }
+
private static class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 61f0b265e874..9ae477ee717d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -797,7 +797,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
true);
verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
- anyString(), anyInt(), any(), eq(CONVERSATION_ID));
+ anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
}
@Test
@@ -817,7 +817,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
null,
true);
verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
- anyString(), anyInt(), any(), eq(CONVERSATION_ID));
+ anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index f513c2d1513b..c62487a830fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.row;
-import static android.app.Notification.FLAG_BUBBLE;
import static android.app.NotificationChannel.USER_LOCKED_IMPORTANCE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
@@ -50,13 +49,10 @@ import android.app.INotificationManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
-import android.app.PendingIntent;
-import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -76,9 +72,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.bubbles.BubblesTestActivity;
-import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
@@ -116,8 +109,6 @@ public class NotificationInfoTest extends SysuiTestCase {
private Set<NotificationChannel> mDefaultNotificationChannelSet = new HashSet<>();
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
- private StatusBarNotification mBubbleSbn;
- private NotificationEntry mBubbleEntry;
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@@ -131,8 +122,6 @@ public class NotificationInfoTest extends SysuiTestCase {
private NotificationBlockingHelperManager mBlockingHelperManager;
@Mock
private VisualStabilityManager mVisualStabilityManager;
- @Mock
- private BubbleController mBubbleController;
@Before
public void setUp() throws Exception {
@@ -143,7 +132,6 @@ public class NotificationInfoTest extends SysuiTestCase {
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
- mDependency.injectTestDependency(BubbleController.class, mBubbleController);
// Inflate the layout
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -185,15 +173,6 @@ public class NotificationInfoTest extends SysuiTestCase {
new Notification(), UserHandle.CURRENT, null, 0);
mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
- PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0,
- new Intent(mContext, BubblesTestActivity.class), 0);
- mBubbleSbn = new SbnBuilder(mSbn).setBubbleMetadata(
- new Notification.BubbleMetadata.Builder()
- .setIntent(bubbleIntent)
- .setIcon(Icon.createWithResource(mContext, R.drawable.android)).build())
- .build();
- mBubbleEntry = new NotificationEntryBuilder().setSbn(mBubbleSbn).build();
-
Settings.Secure.putInt(mContext.getContentResolver(),
NOTIFICATION_NEW_INTERRUPTION_MODEL, 1);
}
@@ -765,7 +744,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
- mBubbleEntry,
+ mEntry,
null,
null,
null,
@@ -785,7 +764,7 @@ public class NotificationInfoTest extends SysuiTestCase {
TEST_PACKAGE_NAME,
mNotificationChannel,
mNotificationChannelSet,
- mBubbleEntry,
+ mEntry,
null,
null,
null,
@@ -797,162 +776,6 @@ public class NotificationInfoTest extends SysuiTestCase {
}
@Test
- public void testBindNotification_bubbleIsSelected() throws Exception {
- mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mBubbleEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
-
- View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
- assertEquals(View.VISIBLE, bubbleView.getVisibility());
- assertTrue(bubbleView.isSelected());
- }
-
- @Test
- public void testBindNotification_whenCanBubble() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mBubbleEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
-
- View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
- assertEquals(View.VISIBLE, bubbleView.getVisibility());
- assertFalse(bubbleView.isSelected());
- }
-
- @Test
- public void testBindNotification_whenCantBubble() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
- View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
- assertEquals(View.GONE, bubbleView.getVisibility());
- }
-
- @Test
- public void testBubble_promotesBubble() throws Exception {
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mBubbleEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
-
- assertFalse(mBubbleEntry.isBubble());
-
- // Promote it
- mNotificationInfo.findViewById(R.id.bubble).performClick();
- mNotificationInfo.findViewById(R.id.done).performClick();
- mNotificationInfo.handleCloseControls(true, false);
-
- verify(mBubbleController, times(1)).onUserCreatedBubbleFromNotification(mBubbleEntry);
- }
-
- @Test
- public void testAlert_demotesBubble() throws Exception {
- mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
-
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mBubbleEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
-
- assertTrue(mBubbleEntry.isBubble());
-
- // Demote it
- mNotificationInfo.findViewById(R.id.alert).performClick();
- mNotificationInfo.findViewById(R.id.done).performClick();
- mNotificationInfo.handleCloseControls(true, false);
-
- verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
- }
-
- @Test
- public void testSilence_demotesBubble() throws Exception {
- mBubbleEntry.getSbn().getNotification().flags |= FLAG_BUBBLE;
-
- mNotificationInfo.bindNotification(
- mMockPackageManager,
- mMockINotificationManager,
- mVisualStabilityManager,
- TEST_PACKAGE_NAME,
- mNotificationChannel,
- mNotificationChannelSet,
- mBubbleEntry,
- null,
- null,
- null,
- true,
- false,
- IMPORTANCE_DEFAULT,
- true);
-
- assertTrue(mBubbleEntry.isBubble());
-
- // Demote it
- mNotificationInfo.findViewById(R.id.silence).performClick();
- mNotificationInfo.findViewById(R.id.done).performClick();
- mNotificationInfo.handleCloseControls(true, false);
-
- verify(mBubbleController, times(1)).onUserDemotedBubbleFromNotification(mBubbleEntry);
- }
-
- @Test
public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
mNotificationInfo.bindNotification(
mMockPackageManager,
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 21100458adc1..ad802ff879f2 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -240,6 +240,10 @@ message SystemMessage {
// Inform the user that EAP failure occurs
NOTE_WIFI_EAP_FAILURE = 57;
+ // Notify the user that their softap disabled because auto shutdown timeout expired.
+ // Package: android
+ NOTE_SOFTAP_AUTO_DISABLED = 58;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c9fdd5ae407a..f3a415ef4cb5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -94,6 +94,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityManager;
import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.accessibility.IWindowMagnificationConnection;
import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
@@ -106,6 +107,7 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -204,6 +206,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private AccessibilityInputFilter mInputFilter;
+ private WindowMagnificationManager mWindowMagnificationMgr;
+
private boolean mHasInputFilter;
private KeyEventDispatcher mKeyEventDispatcher;
@@ -877,11 +881,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
*/
@Override
public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR_SERVICE);
- }
+ mSecurityPolicy.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE);
synchronized (mLock) {
notifyAccessibilityButtonVisibilityChangedLocked(shown);
}
@@ -1883,11 +1884,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) {
+ final String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userState.mUserId);
final Set<String> targetsFromSetting = new ArraySet<>();
- readColonDelimitedSettingToSet(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- userState.mUserId, targetsFromSetting, str -> str);
- if (targetsFromSetting.isEmpty()) {
- // Fall back to device's default a11y service.
+ readColonDelimitedStringToSet(settingValue, targetsFromSetting, false, str -> str);
+ // Fall back to device's default a11y service, only when setting is never updated.
+ if (settingValue == null) {
final String defaultService = mContext.getString(
R.string.config_defaultAccessibilityService);
if (!TextUtils.isEmpty(defaultService)) {
@@ -2588,6 +2590,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@Override
+ public void setWindowMagnificationConnection(
+ IWindowMagnificationConnection connection) throws RemoteException {
+ mSecurityPolicy.enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR_SERVICE);
+
+ getWindowMagnificationMgr().setConnection(connection);
+ }
+
+ WindowMagnificationManager getWindowMagnificationMgr() {
+ synchronized (mLock) {
+ if (mWindowMagnificationMgr == null) {
+ mWindowMagnificationMgr = new WindowMagnificationManager();
+ }
+ return mWindowMagnificationMgr;
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 7dbec7c8c0c5..7a42cd1b3cbb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -550,4 +550,17 @@ public class AccessibilitySecurityPolicy {
Binder.restoreCallingIdentity(identityToken);
}
}
+
+ /**
+ * Enforcing permission check to IPC caller or grant it if it's not through IPC.
+ *
+ * @param permission The permission to check
+ */
+ public void enforceCallingOrSelfPermission(@NonNull String permission) {
+ if (mContext.checkCallingOrSelfPermission(permission)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Caller does not hold permission "
+ + permission);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
new file mode 100644
index 000000000000..351c9e08b645
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapper.java
@@ -0,0 +1,110 @@
+/*
+ * 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.server.accessibility.magnification;
+
+import static android.os.IBinder.DeathRecipient;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+/**
+ * A wrapper of {@link IWindowMagnificationConnection}.
+ */
+class WindowMagnificationConnectionWrapper {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "WindowMagnificationConnectionWrapper";
+
+ private final @NonNull IWindowMagnificationConnection mConnection;
+
+ WindowMagnificationConnectionWrapper(@NonNull IWindowMagnificationConnection connection) {
+ mConnection = connection;
+ }
+
+ //Should not use this instance anymore after calling it.
+ void unlinkToDeath(@NonNull DeathRecipient deathRecipient) {
+ mConnection.asBinder().unlinkToDeath(deathRecipient, 0);
+ }
+
+ void linkToDeath(@NonNull DeathRecipient deathRecipient) throws RemoteException {
+ mConnection.asBinder().linkToDeath(deathRecipient, 0);
+ }
+
+ boolean enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
+ try {
+ mConnection.enableWindowMagnification(displayId, scale, centerX, centerY);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling enableWindowMagnification()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean setScale(int displayId, float scale) {
+ try {
+ mConnection.setScale(displayId, scale);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling setScale()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean disableWindowMagnification(int displayId) {
+ try {
+ mConnection.disableWindowMagnification(displayId);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling disableWindowMagnification()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+ try {
+ mConnection.moveWindowMagnifier(displayId, offsetX, offsetY);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling moveWindowMagnifier()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ boolean setConnectionCallback(IWindowMagnificationConnectionCallback connectionCallback) {
+ try {
+ mConnection.setConnectionCallback(connectionCallback);
+ } catch (RemoteException e) {
+ if (DBG) {
+ Slog.e(TAG, "Error calling setConnectionCallback()");
+ }
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
new file mode 100644
index 000000000000..00db3294c9e6
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -0,0 +1,98 @@
+/*
+ * 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.server.accessibility.magnification;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper}.
+ */
+public final class WindowMagnificationManager {
+
+ private static final String TAG = "WindowMagnificationMgr";
+ private final Object mLock = new Object();
+ @VisibleForTesting
+ @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper;
+ private ConnectionCallback mConnectionCallback;
+
+ /**
+ * Sets {@link IWindowMagnificationConnection}.
+ * @param connection {@link IWindowMagnificationConnection}
+ */
+ public void setConnection(@Nullable IWindowMagnificationConnection connection) {
+ synchronized (mLock) {
+ //Reset connectionWrapper.
+ if (mConnectionWrapper != null) {
+ mConnectionWrapper.setConnectionCallback(null);
+ if (mConnectionCallback != null) {
+ mConnectionCallback.mExpiredDeathRecipient = true;
+ }
+ mConnectionWrapper.unlinkToDeath(mConnectionCallback);
+ mConnectionWrapper = null;
+ }
+ if (connection != null) {
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection);
+ }
+
+ if (mConnectionWrapper != null) {
+ try {
+ mConnectionCallback = new ConnectionCallback();
+ mConnectionWrapper.linkToDeath(mConnectionCallback);
+ mConnectionWrapper.setConnectionCallback(mConnectionCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "setConnection failed", e);
+ mConnectionWrapper = null;
+ }
+ }
+ }
+ }
+
+ private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
+ IBinder.DeathRecipient {
+ private boolean mExpiredDeathRecipient = false;
+
+ @Override
+ public void onWindowMagnifierBoundsChanged(int display, Rect frame) throws RemoteException {
+ }
+
+ @Override
+ public void onChangeMagnificationMode(int display, int magnificationMode)
+ throws RemoteException {
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (mExpiredDeathRecipient) {
+ Slog.w(TAG, "binderDied DeathRecipient is expired");
+ return;
+ }
+ mConnectionWrapper.unlinkToDeath(this);
+ mConnectionWrapper = null;
+ mConnectionCallback = null;
+ }
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2c229b443f91..f0fa99a4eec7 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -31,6 +31,7 @@ import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSI
import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
+import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
@@ -111,7 +112,6 @@ import com.android.server.backup.internal.ClearDataObserver;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.internal.Operation;
import com.android.server.backup.internal.PerformInitializeTask;
-import com.android.server.backup.internal.RunBackupReceiver;
import com.android.server.backup.internal.RunInitializeReceiver;
import com.android.server.backup.internal.SetupObserver;
import com.android.server.backup.keyvalue.BackupRequest;
@@ -257,7 +257,6 @@ public class UserBackupManagerService {
// Retry interval for clear/init when the transport is unavailable
private static final long TRANSPORT_RETRY_INTERVAL = 1 * AlarmManager.INTERVAL_HOUR;
- public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
@@ -319,7 +318,6 @@ public class UserBackupManagerService {
private boolean mSetupComplete;
private boolean mAutoRestore;
- private final PendingIntent mRunBackupIntent;
private final PendingIntent mRunInitIntent;
private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
@@ -417,7 +415,6 @@ public class UserBackupManagerService {
@Nullable private File mAncestralSerialNumberFile;
private final ContentObserver mSetupObserver;
- private final BroadcastReceiver mRunBackupReceiver;
private final BroadcastReceiver mRunInitReceiver;
/**
@@ -566,19 +563,9 @@ public class UserBackupManagerService {
mDataDir = Objects.requireNonNull(dataDir, "dataDir cannot be null");
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
- // Receivers for scheduled backups and transport initialization operations.
- mRunBackupReceiver = new RunBackupReceiver(this);
- IntentFilter filter = new IntentFilter();
- filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiverAsUser(
- mRunBackupReceiver,
- UserHandle.of(userId),
- filter,
- android.Manifest.permission.BACKUP,
- /* scheduler */ null);
-
+ // Receiver for transport initialization.
mRunInitReceiver = new RunInitializeReceiver(this);
- filter = new IntentFilter();
+ IntentFilter filter = new IntentFilter();
filter.addAction(RUN_INITIALIZE_ACTION);
context.registerReceiverAsUser(
mRunInitReceiver,
@@ -587,16 +574,6 @@ public class UserBackupManagerService {
android.Manifest.permission.BACKUP,
/* scheduler */ null);
- Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
- backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent =
- PendingIntent.getBroadcastAsUser(
- context,
- /* requestCode */ 0,
- backupIntent,
- /* flags */ 0,
- UserHandle.of(userId));
-
Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mRunInitIntent =
@@ -659,7 +636,6 @@ public class UserBackupManagerService {
mAgentTimeoutParameters.stop();
mConstants.stop();
mContext.getContentResolver().unregisterContentObserver(mSetupObserver);
- mContext.unregisterReceiver(mRunBackupReceiver);
mContext.unregisterReceiver(mRunInitReceiver);
mContext.unregisterReceiver(mPackageTrackingReceiver);
mBackupHandler.stop();
@@ -2538,15 +2514,38 @@ public class UserBackupManagerService {
KeyValueBackupJob.schedule(mUserId, mContext, mConstants);
} else {
if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
- // Fire the intent that kicks off the whole shebang...
- try {
- mRunBackupIntent.send();
- } catch (PendingIntent.CanceledException e) {
- // should never happen
- Slog.e(TAG, "run-backup intent cancelled!");
+
+ synchronized (getQueueLock()) {
+ if (getPendingInits().size() > 0) {
+ // If there are pending init operations, we process those and then settle
+ // into the usual periodic backup schedule.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Init pending at scheduled backup");
+ }
+ try {
+ getAlarmManager().cancel(mRunInitIntent);
+ mRunInitIntent.send();
+ } catch (PendingIntent.CanceledException ce) {
+ Slog.w(TAG, "Run init intent cancelled");
+ }
+ return;
}
- // ...and cancel any pending scheduled job, because we've just superseded it
- KeyValueBackupJob.cancel(mUserId, mContext);
+ }
+
+ // Don't run backups if we're disabled or not yet set up.
+ if (!isEnabled() || !isSetupComplete()) {
+ Slog.w(
+ TAG,
+ "Backup pass but enabled=" + isEnabled()
+ + " setupComplete=" + isSetupComplete());
+ return;
+ }
+
+ // Fire the msg that kicks off the whole shebang...
+ Message message = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+ mBackupHandler.sendMessage(message);
+ // ...and cancel any pending scheduled job, because we've just superseded it
+ KeyValueBackupJob.cancel(mUserId, mContext);
}
} finally {
Binder.restoreCallingIdentity(oldId);
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index b06fc52a24c2..05396f36b364 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -158,10 +158,6 @@ public class BackupHandler extends Handler {
.disposeOfTransportClient(transportClient, callerLogString);
}
Slog.v(TAG, "Backup requested but no transport available");
- synchronized (backupManagerService.getQueueLock()) {
- backupManagerService.setBackupRunning(false);
- }
- backupManagerService.getWakelock().release();
break;
}
@@ -169,6 +165,21 @@ public class BackupHandler extends Handler {
List<String> queue = new ArrayList<>();
DataChangedJournal oldJournal = backupManagerService.getJournal();
synchronized (backupManagerService.getQueueLock()) {
+ // Don't run backups if one is already running.
+ if (backupManagerService.isBackupRunning()) {
+ Slog.i(TAG, "Backup time but one already running");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Running a backup pass");
+ }
+
+ // Acquire the wakelock and pass it to the backup thread. It will be released
+ // once backup concludes.
+ backupManagerService.setBackupRunning(true);
+ backupManagerService.getWakelock().acquire();
+
// Do we have any work to do? Construct the work queue
// then release the synchronization lock to actually run
// the backup.
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
deleted file mode 100644
index d37b106c2b26..000000000000
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ /dev/null
@@ -1,110 +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.backup.internal;
-
-import static com.android.server.backup.BackupManagerService.DEBUG;
-import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.UserBackupManagerService.RUN_BACKUP_ACTION;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.Message;
-import android.util.Slog;
-
-import com.android.server.backup.UserBackupManagerService;
-
-/**
- * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION}
- * that runs an immediate backup operation if eligible.
- */
-public class RunBackupReceiver extends BroadcastReceiver {
- private final UserBackupManagerService mUserBackupManagerService;
-
- public RunBackupReceiver(UserBackupManagerService userBackupManagerService) {
- mUserBackupManagerService = userBackupManagerService;
- }
-
- /**
- * Run a backup pass if we're eligible. We're eligible if the following conditions are met:
- *
- * <ul>
- * <li>No transports are pending initialization (otherwise we kick off an initialization
- * operation instead).
- * <li>Backup is enabled for the user.
- * <li>The user has completed setup.
- * <li>No backup operation is currently running for the user.
- * </ul>
- */
- public void onReceive(Context context, Intent intent) {
- if (!RUN_BACKUP_ACTION.equals(intent.getAction())) {
- return;
- }
-
- synchronized (mUserBackupManagerService.getQueueLock()) {
- if (mUserBackupManagerService.getPendingInits().size() > 0) {
- // If there are pending init operations, we process those and then settle into the
- // usual periodic backup schedule.
- if (MORE_DEBUG) {
- Slog.v(TAG, "Init pending at scheduled backup");
- }
- try {
- PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent();
- mUserBackupManagerService.getAlarmManager().cancel(runInitIntent);
- runInitIntent.send();
- } catch (PendingIntent.CanceledException ce) {
- Slog.w(TAG, "Run init intent cancelled");
- }
- } else {
- // Don't run backups if we're disabled or not yet set up.
- if (!mUserBackupManagerService.isEnabled()
- || !mUserBackupManagerService.isSetupComplete()) {
- Slog.w(
- TAG,
- "Backup pass but enabled="
- + mUserBackupManagerService.isEnabled()
- + " setupComplete="
- + mUserBackupManagerService.isSetupComplete());
- return;
- }
-
- // Don't run backups if one is already running.
- if (mUserBackupManagerService.isBackupRunning()) {
- Slog.i(TAG, "Backup time but one already running");
- return;
- }
-
- if (DEBUG) {
- Slog.v(TAG, "Running a backup pass");
- }
-
- // Acquire the wakelock and pass it to the backup thread. It will be released once
- // backup concludes.
- mUserBackupManagerService.setBackupRunning(true);
- mUserBackupManagerService.getWakelock().acquire();
-
- Handler backupHandler = mUserBackupManagerService.getBackupHandler();
- Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP);
- backupHandler.sendMessage(message);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1e23e42c14f..5e495b98c58a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -39,6 +39,7 @@ 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;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -5840,11 +5841,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (nai.isSuspended()) {
- newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- } else {
- newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- }
if (nai.partialConnectivity) {
newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
} else {
@@ -5852,6 +5848,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
+ // TODO : remove this once all factories are updated to send NOT_SUSPENDED
+ if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
+
return newNc;
}
@@ -5896,6 +5897,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -5903,6 +5915,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+ // never returns null), so mark the relevant members and functions in nai as @NonNull and
+ // remove this test
if (prevNc != null) {
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -6597,10 +6612,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
- private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+ @NonNull
+ private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+ final NetworkInfo newInfo = new NetworkInfo(info);
+ // The suspended bit is managed in NetworkCapabilities.
+ final boolean suspended =
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+ // Only override the state with SUSPENDED if the network is currently in CONNECTED
+ // state. This is because the network could have been suspended before connecting,
+ // or it could be disconnecting while being suspended, and in both these cases
+ // the state should not be overridden. Note that the only detailed state that
+ // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+ // worry about multiple different substates of CONNECTED.
+ newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+ info.getExtraInfo());
+ }
+ return newInfo;
+ }
+
+ private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+ final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
- final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
@@ -6682,17 +6717,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
} else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
state == NetworkInfo.State.SUSPENDED)) {
- // going into or coming out of SUSPEND: re-score and notify
- if (networkAgent.getCurrentScore() != oldScore) {
- rematchAllNetworksAndRequests();
- }
- updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
- networkAgent.networkCapabilities);
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
- ConnectivityManager.CALLBACK_SUSPENDED :
- ConnectivityManager.CALLBACK_RESUMED));
mLegacyTypeTracker.update(networkAgent);
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index c1ab55106ab1..d66aec576137 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -451,15 +451,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
&& !isLingering();
}
- /**
- * Returns whether this network is currently suspended. A network is suspended if it is still
- * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
- * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
- */
- public boolean isSuspended() {
- return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
- }
-
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index bc7307b3ee6c..74c1e63172fa 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -16,6 +16,8 @@
package com.android.server.content;
+import static android.content.PermissionChecker.PERMISSION_GRANTED;
+
import android.Manifest;
import android.accounts.Account;
import android.annotation.Nullable;
@@ -1212,7 +1214,7 @@ public final class ContentService extends IContentService.Stub {
@RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
public void putCache(String packageName, Uri key, Bundle value, int userId) {
Bundle.setDefusable(value, true);
- enforceCrossUserPermission(userId, TAG);
+ enforceNonFullCrossUserPermission(userId, TAG);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
packageName);
@@ -1234,7 +1236,7 @@ public final class ContentService extends IContentService.Stub {
@Override
@RequiresPermission(android.Manifest.permission.CACHE_CONTENT)
public Bundle getCache(String packageName, Uri key, int userId) {
- enforceCrossUserPermission(userId, TAG);
+ enforceNonFullCrossUserPermission(userId, TAG);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG);
mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
packageName);
@@ -1302,6 +1304,30 @@ public final class ContentService extends IContentService.Stub {
}
}
+ /**
+ * Checks if the request is from the system or an app that has {@code INTERACT_ACROSS_USERS} or
+ * {@code INTERACT_ACROSS_USERS_FULL} permission, if the {@code userHandle} is not for the
+ * caller.
+ *
+ * @param userHandle the user handle of the user we want to act on behalf of.
+ * @param message the message to log on security exception.
+ */
+ private void enforceNonFullCrossUserPermission(int userHandle, String message) {
+ final int callingUser = UserHandle.getCallingUserId();
+ if (callingUser == userHandle) {
+ return;
+ }
+
+ int interactAcrossUsersState = mContext.checkCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS);
+ if (interactAcrossUsersState == PERMISSION_GRANTED) {
+ return;
+ }
+
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ }
+
private static int normalizeSyncable(int syncable) {
if (syncable > 0) {
return SyncStorageEngine.AuthorityInfo.SYNCABLE;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 1794df3b602e..7c2ec78c1cbc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -491,7 +491,7 @@ public class HdmiControlService extends SystemService {
mIoThread.start();
mIoLooper = mIoThread.getLooper();
}
- mPowerStatus = HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+ mPowerStatus = getInitialPowerStatus();
mProhibitMode = false;
mHdmiControlEnabled = readBooleanSetting(Global.HDMI_CONTROL_ENABLED, true);
mMhlInputChangeEnabled = readBooleanSetting(Global.MHL_INPUT_SWITCHING_ENABLED, true);
@@ -538,6 +538,28 @@ public class HdmiControlService extends SystemService {
mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, ENABLED);
}
+ private void bootCompleted() {
+ // on boot, if device is interactive, set HDMI CEC state as powered on as well
+ if (mPowerManager.isInteractive() && isPowerStandbyOrTransient()) {
+ onWakeUp();
+ }
+ }
+
+ /**
+ * Returns the initial power status used when the HdmiControlService starts.
+ */
+ @VisibleForTesting
+ int getInitialPowerStatus() {
+ // The initial power status is POWER_STATUS_TRANSIENT_TO_STANDBY.
+ // Once boot completes the service transitions to POWER_STATUS_ON if the device is
+ // interactive.
+ // Quiescent boot is a special boot mode, in which the screen stays off during boot
+ // and the device goes to sleep after boot has finished.
+ // We don't transition to POWER_STATUS_ON initially, as we might be booting in quiescent
+ // mode, during which we don't want to appear powered on to avoid being made active source.
+ return HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY;
+ }
+
@VisibleForTesting
void setCecController(HdmiCecController cecController) {
mCecController = cecController;
@@ -553,7 +575,9 @@ public class HdmiControlService extends SystemService {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mTvInputManager = (TvInputManager) getContext().getSystemService(
Context.TV_INPUT_SERVICE);
- mPowerManager = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+ mPowerManager = getContext().getSystemService(PowerManager.class);
+ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ runOnServiceThread(this::bootCompleted);
}
}
@@ -579,9 +603,7 @@ public class HdmiControlService extends SystemService {
* Called when the initialization of local devices is complete.
*/
private void onInitializeCecComplete(int initiatedBy) {
- if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
- mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
- }
+ updatePowerStatusOnInitializeCecComplete();
mWakeUpMessageReceived = false;
if (isTvDeviceEnabled()) {
@@ -606,6 +628,17 @@ public class HdmiControlService extends SystemService {
}
}
+ /**
+ * Updates the power status once the initialization of local devices is complete.
+ */
+ private void updatePowerStatusOnInitializeCecComplete() {
+ if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON) {
+ mPowerStatus = HdmiControlManager.POWER_STATUS_ON;
+ } else if (mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY) {
+ mPowerStatus = HdmiControlManager.POWER_STATUS_STANDBY;
+ }
+ }
+
private void registerContentObserver() {
ContentResolver resolver = getContext().getContentResolver();
String[] settings = new String[] {
@@ -2667,6 +2700,13 @@ public class HdmiControlService extends SystemService {
}
@ServiceThreadOnly
+ @VisibleForTesting
+ void setPowerStatus(int powerStatus) {
+ assertRunOnServiceThread();
+ mPowerStatus = powerStatus;
+ }
+
+ @ServiceThreadOnly
boolean isPowerOnOrTransient() {
assertRunOnServiceThread();
return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 6afadbad2054..f5ed975bf772 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -56,6 +56,9 @@ import java.util.stream.Collectors;
/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
+ static final int TOTAL_RULE_SIZE_LIMIT = 200000;
+ static final int INDEXED_RULE_SIZE_LIMIT = 100000;
+ static final int NONINDEXED_RULE_SIZE_LIMIT = 1000;
// Get the byte representation for a list of rules.
@Override
@@ -79,10 +82,23 @@ public class RuleBinarySerializer implements RuleSerializer {
OutputStream indexingFileOutputStream)
throws RuleSerializeException {
try {
+ if (rules == null) {
+ throw new IllegalArgumentException("Null rules cannot be serialized.");
+ }
+
+ if (rules.size() > TOTAL_RULE_SIZE_LIMIT) {
+ throw new IllegalArgumentException("Too many rules provided.");
+ }
+
// Determine the indexing groups and the order of the rules within each indexed group.
Map<Integer, Map<String, List<Rule>>> indexedRules =
RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
+ // Validate the rule blocks are not larger than expected limits.
+ verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT);
+ verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT);
+ verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT);
+
// Serialize the rules.
ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
new ByteTrackedOutputStream(rulesFileOutputStream);
@@ -112,6 +128,16 @@ public class RuleBinarySerializer implements RuleSerializer {
}
}
+ private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) {
+ int totalRuleCount =
+ ruleListMap.values().stream()
+ .map(list -> list.size())
+ .collect(Collectors.summingInt(Integer::intValue));
+ if (totalRuleCount > ruleSizeLimit) {
+ throw new IllegalArgumentException("Too many rules provided in the indexing group.");
+ }
+ }
+
private void serializeRuleFileMetadata(
Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream)
throws IOException {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index b186771031fc..1cd8aad3f0c5 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -22,6 +22,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.media.MediaRoute2ProviderInfo;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import com.android.internal.annotations.GuardedBy;
@@ -49,7 +50,8 @@ abstract class MediaRoute2Provider {
mCallback = callback;
}
- public abstract void requestCreateSession(String packageName, String routeId, long requestId);
+ public abstract void requestCreateSession(String packageName, String routeId, long requestId,
+ @Nullable Bundle sessionHints);
public abstract void releaseSession(String sessionId);
public abstract void selectRoute(String sessionId, String routeId);
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 4b992be46792..97614619653e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -26,6 +26,7 @@ import android.media.IMediaRoute2ProviderClient;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
import android.media.RoutingSessionInfo;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
@@ -73,9 +74,11 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
if (mConnectionReady) {
- mActiveConnection.requestCreateSession(packageName, routeId, requestId);
+ mActiveConnection.requestCreateSession(
+ packageName, routeId, requestId, sessionHints);
updateBinding();
}
}
@@ -427,9 +430,10 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv
mClient.dispose();
}
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
try {
- mProvider.requestCreateSession(packageName, routeId, requestId);
+ mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to deliver request to create a session.", ex);
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 45d50b33357e..216753017010 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -22,6 +22,7 @@ import static android.media.MediaRouter2Utils.getProviderId;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -63,12 +64,12 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * TODO: Merge this to MediaRouterService once it's finished.
+ * Implements features related to {@link android.media.MediaRouter2} and
+ * {@link android.media.MediaRouter2Manager}.
*/
class MediaRouter2ServiceImpl {
private static final String TAG = "MR2ServiceImpl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final long ROUTE_SELECTION_REQUEST_TIMEOUT_MS = 5000L;
private final Context mContext;
private final Object mLock = new Object();
@@ -178,14 +179,14 @@ class MediaRouter2ServiceImpl {
}
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- int requestId) {
+ int requestId, Bundle sessionHints) {
Objects.requireNonNull(client, "client must not be null");
Objects.requireNonNull(route, "route must not be null");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestCreateSessionLocked(client, route, requestId);
+ requestCreateSessionLocked(client, route, requestId, sessionHints);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -377,6 +378,53 @@ class MediaRouter2ServiceImpl {
}
}
+ public void selectClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ selectClientRouteLocked(manager, sessionId, route);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ deselectClientRouteLocked(manager, sessionId, route);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ transferClientRouteLocked(manager, sessionId, route);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ releaseClientSessionLocked(manager, sessionId);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
//TODO: Review this is handling multi-user properly.
void switchUser() {
synchronized (mLock) {
@@ -417,6 +465,7 @@ class MediaRouter2ServiceImpl {
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = client.asBinder();
if (mAllClientRecords.get(binder) == null) {
+
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid,
packageName, trusted);
@@ -446,7 +495,7 @@ class MediaRouter2ServiceImpl {
}
private void requestCreateSessionLocked(@NonNull IMediaRouter2Client client,
- @NonNull MediaRoute2Info route, long requestId) {
+ @NonNull MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
final IBinder binder = client.asBinder();
final Client2Record clientRecord = mAllClientRecords.get(binder);
@@ -458,7 +507,8 @@ class MediaRouter2ServiceImpl {
if (clientRecord != null) {
clientRecord.mUserRecord.mHandler.sendMessage(
obtainMessage(UserHandler::requestCreateSessionOnHandler,
- clientRecord.mUserRecord.mHandler, clientRecord, route, requestId));
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, route, requestId, sessionHints));
}
}
@@ -617,9 +667,9 @@ class MediaRouter2ServiceImpl {
}
long uniqueRequestId = toUniqueRequestId(managerRecord.mClientId, requestId);
if (clientRecord != null && managerRecord.mTrusted) {
- //TODO: select route feature properly
+ //TODO: Use client's OnCreateSessionListener to send proper session hints.
requestCreateSessionLocked(clientRecord.mClient, route,
- uniqueRequestId);
+ uniqueRequestId, null /* sessionHints */);
}
}
}
@@ -653,6 +703,7 @@ class MediaRouter2ServiceImpl {
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord == null) {
+ Slog.w(TAG, "getActiveSessionLocked: Ignoring unknown manager");
return Collections.emptyList();
}
@@ -676,6 +727,93 @@ class MediaRouter2ServiceImpl {
return userRecord;
}
+ private void selectClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord == null) {
+ Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown manager.");
+ return;
+ }
+ //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+ Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+ .findClientforSessionLocked(sessionId);
+ if (clientRecord == null) {
+ Slog.w(TAG, "selectClientRouteLocked: Ignoring unknown session.");
+ return;
+ }
+
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::selectRouteOnHandler,
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, sessionId, route));
+ }
+
+ private void deselectClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord == null) {
+ Slog.w(TAG, "deselectClientRouteLocked: Ignoring unknown manager.");
+ return;
+ }
+ //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+ Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+ .findClientforSessionLocked(sessionId);
+ if (clientRecord == null) {
+ Slog.w(TAG, "deslectClientRouteLocked: Ignoring unknown session.");
+ return;
+ }
+
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::deselectRouteOnHandler,
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, sessionId, route));
+ }
+
+ private void transferClientRouteLocked(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord == null) {
+ Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown manager.");
+ return;
+ }
+ //TODO: we shouldn't ignore selecting request for unknown clients. (RCN?)
+ Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+ .findClientforSessionLocked(sessionId);
+ if (clientRecord == null) {
+ Slog.w(TAG, "transferClientRouteLocked: Ignoring unknown session.");
+ return;
+ }
+
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::transferToRouteOnHandler,
+ clientRecord.mUserRecord.mHandler,
+ clientRecord, sessionId, route));
+ }
+
+ private void releaseClientSessionLocked(IMediaRouter2Manager manager, String sessionId) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord == null) {
+ Slog.w(TAG, "releaseClientSessionLocked: Ignoring unknown manager.");
+ return;
+ }
+
+ Client2Record clientRecord = managerRecord.mUserRecord.mHandler
+ .findClientforSessionLocked(sessionId);
+
+ managerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::releaseSessionOnHandler,
+ managerRecord.mUserRecord.mHandler,
+ clientRecord, sessionId));
+ }
+
private void disposeUserIfNeededLocked(UserRecord userRecord) {
// If there are no records left and the user is no longer current then go ahead
// and purge the user record and all of its associated state. If the user is current
@@ -896,6 +1034,11 @@ class MediaRouter2ServiceImpl {
this, provider, sessionInfo));
}
+ @Nullable
+ public Client2Record findClientforSessionLocked(@NonNull String sessionId) {
+ return mSessionToClientMap.get(sessionId);
+ }
+
//TODO: notify session info updates
private void onProviderStateChangedOnHandler(MediaRoute2Provider provider) {
int providerIndex = getProviderInfoIndex(provider.getUniqueId());
@@ -980,7 +1123,7 @@ class MediaRouter2ServiceImpl {
}
private void requestCreateSessionOnHandler(Client2Record clientRecord,
- MediaRoute2Info route, long requestId) {
+ MediaRoute2Info route, long requestId, @Nullable Bundle sessionHints) {
final MediaRoute2Provider provider = findProvider(route.getProviderId());
if (provider == null) {
@@ -996,7 +1139,7 @@ class MediaRouter2ServiceImpl {
mSessionCreationRequests.add(request);
provider.requestCreateSession(clientRecord.mPackageName, route.getOriginalId(),
- requestId);
+ requestId, sessionHints);
}
private void selectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1128,9 +1271,10 @@ class MediaRouter2ServiceImpl {
private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo, long requestId) {
+ notifySessionCreatedToManagers(getManagers(), sessionInfo);
+
if (requestId == MediaRoute2ProviderService.REQUEST_ID_UNKNOWN) {
// The session is created without any matching request.
- // TODO: Tell managers for the session creation
return;
}
@@ -1176,7 +1320,6 @@ class MediaRouter2ServiceImpl {
notifySessionCreated(matchingRequest.mClientRecord,
sessionInfo, toClientRequestId(requestId));
mSessionToClientMap.put(sessionInfo.getId(), client2Record);
- // TODO: Tell managers for the session creation
}
private void onSessionCreationFailedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -1205,29 +1348,29 @@ class MediaRouter2ServiceImpl {
private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo) {
+ List<IMediaRouter2Manager> managers = getManagers();
+ notifySessionInfosChangedToManagers(managers);
Client2Record client2Record = mSessionToClientMap.get(
sessionInfo.getId());
if (client2Record == null) {
Slog.w(TAG, "No matching client found for session=" + sessionInfo);
- // TODO: Tell managers for the session update
return;
}
notifySessionInfoChanged(client2Record, sessionInfo);
- // TODO: Tell managers for the session update
}
private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
@NonNull RoutingSessionInfo sessionInfo) {
+ List<IMediaRouter2Manager> managers = getManagers();
+ notifySessionInfosChangedToManagers(managers);
Client2Record client2Record = mSessionToClientMap.get(sessionInfo.getId());
if (client2Record == null) {
Slog.w(TAG, "No matching client found for session=" + sessionInfo);
- // TODO: Tell managers for the session release
return;
}
notifySessionReleased(client2Record, sessionInfo);
- // TODO: Tell managers for the session release
}
private void notifySessionCreated(Client2Record clientRecord,
@@ -1333,11 +1476,6 @@ class MediaRouter2ServiceImpl {
}
}
- // TODO: Remove notifyRouteSelected* methods
- private void notifyRouteSelectedToClient(IMediaRouter2Client client,
- MediaRoute2Info route, int reason, Bundle controlHints) {
- }
-
private void notifyRoutesAddedToClients(List<IMediaRouter2Client> clients,
List<MediaRoute2Info> routes) {
for (IMediaRouter2Client client : clients) {
@@ -1419,6 +1557,29 @@ class MediaRouter2ServiceImpl {
}
}
+ private void notifySessionCreatedToManagers(List<IMediaRouter2Manager> managers,
+ RoutingSessionInfo sessionInfo) {
+ for (IMediaRouter2Manager manager : managers) {
+ try {
+ manager.notifySessionCreated(sessionInfo);
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "notifySessionCreatedToManagers: "
+ + "failed to notify. Manager probably died.", ex);
+ }
+ }
+ }
+
+ private void notifySessionInfosChangedToManagers(List<IMediaRouter2Manager> managers) {
+ for (IMediaRouter2Manager manager : managers) {
+ try {
+ manager.notifySessionsUpdated();
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "notifySessionInfosChangedToManagers: "
+ + "failed to notify. Manager probably died.", ex);
+ }
+ }
+ }
+
private void updateClientUsage(Client2Record clientRecord) {
MediaRouter2ServiceImpl service = mServiceRef.get();
if (service == null) {
@@ -1432,8 +1593,6 @@ class MediaRouter2ServiceImpl {
}
for (IMediaRouter2Manager manager : managers) {
try {
- manager.notifyRouteSelected(clientRecord.mPackageName,
- clientRecord.mSelectedRoute);
manager.notifyPreferredFeaturesChanged(clientRecord.mPackageName,
clientRecord.mDiscoveryPreference.getPreferredFeatures());
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index aad963688cc5..5437fadf2d74 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -42,6 +42,7 @@ import android.media.RemoteDisplayState.RemoteDisplayInfo;
import android.media.RouteDiscoveryPreference;
import android.media.RoutingSessionInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -460,8 +461,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
// Binder call
@Override
public void requestCreateSession(IMediaRouter2Client client, MediaRoute2Info route,
- int requestId) {
- mService2.requestCreateSession(client, route, requestId);
+ int requestId, Bundle sessionHints) {
+ mService2.requestCreateSession(client, route, requestId, sessionHints);
}
// Binder call
@@ -556,6 +557,33 @@ public final class MediaRouterService extends IMediaRouterService.Stub
return mService2.getActiveSessions(manager);
}
+ // Binder call
+ @Override
+ public void selectClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ mService2.selectClientRoute(manager, sessionId, route);
+ }
+
+ // Binder call
+ @Override
+ public void deselectClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ mService2.deselectClientRoute(manager, sessionId, route);
+ }
+
+ // Binder call
+ @Override
+ public void transferToClientRoute(IMediaRouter2Manager manager, String sessionId,
+ MediaRoute2Info route) {
+ mService2.transferToClientRoute(manager, sessionId, route);
+ }
+
+ // Binder call
+ @Override
+ public void releaseClientSession(IMediaRouter2Manager manager, String sessionId) {
+ mService2.releaseClientSession(manager, sessionId);
+ }
+
void restoreBluetoothA2dp() {
try {
boolean a2dpOn;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 3759ba9743fa..56c33fe339ea 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -26,6 +26,7 @@ import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -96,7 +97,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
}
@Override
- public void requestCreateSession(String packageName, String routeId, long requestId) {
+ public void requestCreateSession(String packageName, String routeId, long requestId,
+ Bundle sessionHints) {
// Do nothing
}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index eaf120ee2755..0e14364b4280 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -15,7 +15,10 @@
*/
package com.android.server.notification;
+import android.app.Notification;
+import android.app.NotificationChannel;
import android.content.Context;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
/**
@@ -26,8 +29,10 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor
private static final boolean DBG = false;
private RankingConfig mConfig;
+ private Context mContext;
public void initialize(Context ctx, NotificationUsageStats usageStats) {
+ mContext = ctx;
if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
}
@@ -41,11 +46,11 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor
if (DBG) Slog.d(TAG, "missing config");
return null;
}
-
- record.updateNotificationChannel(mConfig.getConversationNotificationChannel(
+ NotificationChannel updatedChannel = mConfig.getConversationNotificationChannel(
record.sbn.getPackageName(),
record.sbn.getUid(), record.getChannel().getId(),
- record.getNotification().getShortcutId(), true, false));
+ record.sbn.getShortcutId(mContext), true, false);
+ record.updateNotificationChannel(updatedChannel);
return null;
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1d493647304f..385d84a93fbd 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -208,6 +208,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -3038,7 +3039,7 @@ public class NotificationManagerService extends SystemService {
@Override
public void createConversationNotificationChannelForPackage(String pkg, int uid,
- NotificationChannel parentChannel, String conversationId) {
+ String triggeringKey, NotificationChannel parentChannel, String conversationId) {
enforceSystemOrSystemUI("only system can call this");
Preconditions.checkNotNull(parentChannel);
Preconditions.checkNotNull(conversationId);
@@ -3049,6 +3050,8 @@ public class NotificationManagerService extends SystemService {
conversationChannel.setConversationId(parentId, conversationId);
createNotificationChannelsImpl(
pkg, uid, new ParceledListSlice(Arrays.asList(conversationChannel)));
+ mRankingHandler.requestSort();
+ handleSavePolicyFile();
}
@Override
@@ -5257,9 +5260,10 @@ public class NotificationManagerService extends SystemService {
if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
channelId = (new Notification.TvExtender(notification)).getChannelId();
}
- // TODO: flag this behavior
String shortcutId = notification.getShortcutId();
- if (shortcutId == null
+ if (FeatureFlagUtils.isEnabled(getContext(),
+ FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ)
+ && shortcutId == null
&& notification.getNotificationStyle() == Notification.MessagingStyle.class) {
shortcutId = id + tag + NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 185d75c754a2..b0c1863ea31d 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.app.NotificationChannel.PLACEHOLDER_CONVERSATION_ID;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
@@ -40,6 +41,7 @@ import android.service.notification.RankingHelperProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -139,6 +141,8 @@ public class PreferencesHelper implements RankingConfig {
private boolean mAreChannelsBypassingDnd;
private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS;
+ private boolean mAllowInvalidShortcuts = false;
+
private static final String BADGING_FORCED_TRUE = "force_badging_true_for_bug";
// STOPSHIP (b/142218092) this should be removed before ship
@@ -164,6 +168,8 @@ public class PreferencesHelper implements RankingConfig {
updateBadgingEnabled();
updateBubblesEnabled();
syncChannelsBypassingDnd(mContext.getUserId());
+ mAllowInvalidShortcuts = FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ);
}
public void readXml(XmlPullParser parser, boolean forRestore, int userId)
@@ -266,7 +272,14 @@ public class PreferencesHelper implements RankingConfig {
}
channel.setImportanceLockedByCriticalDeviceFunction(
r.defaultAppLockedImportance);
- r.channels.put(id, channel);
+ boolean isInvalidShortcutChannel =
+ channel.getConversationId() != null &&
+ channel.getConversationId().contains(
+ PLACEHOLDER_CONVERSATION_ID);
+ if (mAllowInvalidShortcuts || (!mAllowInvalidShortcuts
+ && !isInvalidShortcutChannel)) {
+ r.channels.put(id, channel);
+ }
}
}
// Delegate
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 66a2b013e299..2a249d2c92ec 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -74,6 +74,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
+import android.os.UserManager.QuietModeFlag;
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.os.storage.StorageManager;
@@ -914,7 +915,7 @@ public class UserManagerService extends IUserManager.Stub {
@Override
public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
- @UserIdInt int userId, @Nullable IntentSender target) {
+ @UserIdInt int userId, @Nullable IntentSender target, @QuietModeFlag int flags) {
Objects.requireNonNull(callingPackage);
if (enableQuietMode && target != null) {
@@ -925,24 +926,24 @@ public class UserManagerService extends IUserManager.Stub {
ensureCanModifyQuietMode(callingPackage, Binder.getCallingUid(), userId, target != null);
final long identity = Binder.clearCallingIdentity();
try {
- boolean result = false;
if (enableQuietMode) {
setQuietModeEnabled(
userId, true /* enableQuietMode */, target, callingPackage);
- result = true;
- } else {
- boolean needToShowConfirmCredential =
- mLockPatternUtils.isSecure(userId)
- && !StorageManager.isUserKeyUnlocked(userId);
- if (needToShowConfirmCredential) {
- showConfirmCredentialToDisableQuietMode(userId, target);
- } else {
- setQuietModeEnabled(
- userId, false /* enableQuietMode */, target, callingPackage);
- result = true;
+ return true;
+ }
+ boolean needToShowConfirmCredential =
+ mLockPatternUtils.isSecure(userId)
+ && !StorageManager.isUserKeyUnlocked(userId);
+ if (needToShowConfirmCredential) {
+ if ((flags & UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED) != 0) {
+ return false;
}
+ showConfirmCredentialToDisableQuietMode(userId, target);
+ return false;
}
- return result;
+ setQuietModeEnabled(
+ userId, false /* enableQuietMode */, target, callingPackage);
+ return true;
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7fc9fdc0180d..8460ede91fd0 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -678,9 +678,20 @@ public class Notifier {
final int powerState;
synchronized (mLock) {
if (mBroadcastedInteractiveState == INTERACTIVE_STATE_UNKNOWN) {
- // Broadcasted power state is unknown. Send wake up.
- mPendingWakeUpBroadcast = false;
- mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
+ // Broadcasted power state is unknown.
+ // Send wake up or go to sleep.
+ switch (mPendingInteractiveState) {
+ case INTERACTIVE_STATE_ASLEEP:
+ mPendingGoToSleepBroadcast = false;
+ mBroadcastedInteractiveState = INTERACTIVE_STATE_ASLEEP;
+ break;
+
+ case INTERACTIVE_STATE_AWAKE:
+ default:
+ mPendingWakeUpBroadcast = false;
+ mBroadcastedInteractiveState = INTERACTIVE_STATE_AWAKE;
+ break;
+ }
} else if (mBroadcastedInteractiveState == INTERACTIVE_STATE_AWAKE) {
// Broadcasted power state is awake. Send asleep if needed.
if (mPendingWakeUpBroadcast || mPendingGoToSleepBroadcast
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 93d50b846bc1..c1b71aab38fd 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -346,7 +346,8 @@ public final class PowerManagerService extends SystemService
// True if systemReady() has been called.
private boolean mSystemReady;
- // True if boot completed occurred. We keep the screen on until this happens.
+ // True if boot completed occurred. We keep the screen on until this happens.
+ // The screen will be off if we are in quiescent mode.
private boolean mBootCompleted;
// True if auto-suspend mode is enabled.
@@ -864,6 +865,12 @@ public final class PowerManagerService extends SystemService
mBatterySaverStateMachine.onBootCompleted();
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+
+ if (sQuiescent) {
+ goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
+ }
updatePowerStateLocked();
}
}
@@ -1430,8 +1437,7 @@ public final class PowerManagerService extends SystemService
+ ", uid=" + uid);
}
- if (eventTime < mLastSleepTime || eventTime < mLastWakeTime
- || !mBootCompleted || !mSystemReady) {
+ if (eventTime < mLastSleepTime || eventTime < mLastWakeTime || !mSystemReady) {
return false;
}
@@ -1508,7 +1514,7 @@ public final class PowerManagerService extends SystemService
}
if (eventTime < mLastSleepTime || mWakefulness == WAKEFULNESS_AWAKE
- || !mBootCompleted || !mSystemReady || mForceSuspendActive) {
+ || mForceSuspendActive || !mSystemReady) {
return false;
}
@@ -1530,6 +1536,10 @@ public final class PowerManagerService extends SystemService
mNotifier.onWakeUp(reason, details, reasonUid, opPackageName, opUid);
userActivityNoUpdateLocked(
eventTime, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, reasonUid);
+
+ if (sQuiescent) {
+ mDirty |= DIRTY_QUIESCENT;
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -1561,7 +1571,8 @@ public final class PowerManagerService extends SystemService
if (eventTime < mLastWakeTime
|| mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
- || !mBootCompleted || !mSystemReady) {
+ || !mSystemReady
+ || !mBootCompleted) {
return false;
}
@@ -2645,6 +2656,10 @@ public final class PowerManagerService extends SystemService
| DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED | DIRTY_BOOT_COMPLETED
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
DIRTY_QUIESCENT)) != 0) {
+ if ((dirty & DIRTY_QUIESCENT) != 0) {
+ sQuiescent = false;
+ }
+
mDisplayPowerRequest.policy = getDesiredScreenPolicyLocked();
// Determine appropriate screen brightness and auto-brightness adjustments.
@@ -2694,9 +2709,6 @@ public final class PowerManagerService extends SystemService
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
- if ((dirty & DIRTY_QUIESCENT) != 0) {
- sQuiescent = false;
- }
if (DEBUG_SPEW) {
Slog.d(TAG, "updateDisplayPowerStateLocked: mDisplayReady=" + mDisplayReady
+ ", policy=" + mDisplayPowerRequest.policy
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 3bc860adff0c..1e856884eeed 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -173,7 +173,6 @@ import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -349,14 +348,14 @@ public class StatsPullAtomService extends SystemService {
}
private void registerWifiBytesTransfer() {
int tagId = StatsLog.WIFI_BYTES_TRANSFER;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullWifiBytesTransfer(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullWifiBytesTransfer(atomTag, data)
);
}
@@ -441,14 +440,14 @@ public class StatsPullAtomService extends SystemService {
private void registerWifiBytesTransferBackground() {
int tagId = StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3, 4, 5, 6})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullWifiBytesTransferBackground(atomTag, data)
);
}
@@ -479,14 +478,14 @@ public class StatsPullAtomService extends SystemService {
private void registerMobileBytesTransfer() {
int tagId = StatsLog.MOBILE_BYTES_TRANSFER;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3, 4, 5})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullMobileBytesTransfer(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullMobileBytesTransfer(atomTag, data)
);
}
@@ -517,14 +516,14 @@ public class StatsPullAtomService extends SystemService {
private void registerMobileBytesTransferBackground() {
int tagId = StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3, 4, 5, 6})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullMobileBytesTransferBackground(atomTag, data)
);
}
@@ -555,14 +554,14 @@ public class StatsPullAtomService extends SystemService {
private void registerBluetoothBytesTransfer() {
int tagId = StatsLog.BLUETOOTH_BYTES_TRANSFER;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullBluetoothBytesTransfer(atomTag, data)
);
}
@@ -634,8 +633,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
- (atomTag, data) -> pullKernelWakelock(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullKernelWakelock(atomTag, data)
);
}
@@ -670,14 +669,14 @@ public class StatsPullAtomService extends SystemService {
private void registerCpuTimePerFreq() {
int tagId = StatsLog.CPU_TIME_PER_FREQ;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuTimePerFreq(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullCpuTimePerFreq(atomTag, data)
);
}
@@ -701,14 +700,14 @@ public class StatsPullAtomService extends SystemService {
private void registerCpuTimePerUid() {
int tagId = StatsLog.CPU_TIME_PER_UID;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2, 3})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuTimePerUid(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullCpuTimePerUid(atomTag, data)
);
}
@@ -730,14 +729,14 @@ public class StatsPullAtomService extends SystemService {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = StatsLog.CPU_TIME_PER_UID_FREQ;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {4})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullCpuTimeperUidFreq(atomTag, data)
);
}
@@ -762,14 +761,14 @@ public class StatsPullAtomService extends SystemService {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = StatsLog.CPU_ACTIVE_TIME;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {2})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuActiveTime(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullCpuActiveTime(atomTag, data)
);
}
@@ -789,14 +788,14 @@ public class StatsPullAtomService extends SystemService {
// the throttling is 3sec, handled in
// frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
int tagId = StatsLog.CPU_CLUSTER_TIME;
- PullAtomMetadata metadata = PullAtomMetadata.newBuilder()
+ PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[] {3})
.build();
mStatsManager.registerPullAtomCallback(
tagId,
metadata,
- (atomTag, data) -> pullCpuClusterTime(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullCpuClusterTime(atomTag, data)
);
}
@@ -913,8 +912,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
/* metadata */ null,
- (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullBluetoothActivityInfo(atomTag, data)
);
}
@@ -949,8 +948,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullSystemUptime(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullSystemUptime(atomTag, data)
);
}
@@ -1232,8 +1231,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
- (atomTag, data) -> pullIonHeapSize(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullIonHeapSize(atomTag, data)
);
}
@@ -1511,8 +1510,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
/* PullAtomMetadata */ null,
- (atomTag, data) -> pullPowerProfile(atomTag, data),
- Executors.newSingleThreadExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullPowerProfile(atomTag, data)
);
}
@@ -1681,8 +1680,8 @@ public class StatsPullAtomService extends SystemService {
mStatsManager.registerPullAtomCallback(
tagId,
null, // use default PullAtomMetadata values
- (atomTag, data) -> pullBuildInformation(atomTag, data),
- BackgroundThread.getExecutor()
+ BackgroundThread.getExecutor(),
+ (atomTag, data) -> pullBuildInformation(atomTag, data)
);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3f3408f4d202..b596d2a207f0 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1680,6 +1680,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (root == this) {
task.setRootProcess(proc);
}
+ proc.addActivityIfNeeded(this);
}
boolean hasProcess() {
@@ -6918,7 +6919,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- setLastReportedConfiguration(mAtmService.getGlobalConfiguration(), newMergedOverrideConfig);
+ setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
@@ -7017,6 +7018,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return true;
}
+ /** Get process configuration, or global config if the process is not set. */
+ private Configuration getProcessGlobalConfiguration() {
+ return app != null ? app.getConfiguration() : mAtmService.getGlobalConfiguration();
+ }
+
/**
* When assessing a configuration change, decide if the changes flags and the new configurations
* should cause the Activity to relaunch.
@@ -7129,7 +7135,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
startRelaunching();
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
- new MergedConfiguration(mAtmService.getGlobalConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index aa90248b97f8..0a68408d49a5 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -848,8 +848,6 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
- proc.addActivityIfNeeded(r);
-
final LockTaskController lockTaskController = mService.getLockTaskController();
if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
|| task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e308f6b8f3ce..47e8b87dcf08 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6743,7 +6743,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return;
}
process.mIsImeProcess = true;
- process.registerDisplayConfigurationListenerLocked(displayContent);
+ process.registerDisplayConfigurationListener(displayContent);
}
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 1e60ce83b5fb..7b23e2d383b6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -134,8 +134,8 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+ onMergedOverrideConfigurationChanged();
if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
- onMergedOverrideConfigurationChanged();
// This depends on the assumption that change-listeners don't do
// their own override resolution. This way, dependent hierarchies
// can stay properly synced-up with a primary hierarchy's constraints.
@@ -147,6 +147,10 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
mResolvedOverrideConfiguration);
}
}
+ for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
+ mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
+ mMergedOverrideConfiguration);
+ }
if (forwardToChildren) {
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
@@ -545,6 +549,7 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
}
mChangeListeners.add(listener);
listener.onRequestedOverrideConfigurationChanged(mResolvedOverrideConfiguration);
+ listener.onMergedOverrideConfigurationChanged(mMergedOverrideConfiguration);
}
void unregisterConfigurationChangeListener(ConfigurationContainerListener listener) {
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
index dc4939d55bfa..3d84e1752e6a 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainerListener.java
@@ -24,5 +24,8 @@ import android.content.res.Configuration;
public interface ConfigurationContainerListener {
/** {@see ConfigurationContainer#onRequestedOverrideConfigurationChanged} */
- void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration);
+ default void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {}
+
+ /** Called when new merged override configuration is reported. */
+ default void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfiguration) {}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9a40b1b1a74a..ceb38f7d9789 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.INVALID_DISPLAY;
@@ -178,8 +179,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration;
+ private final Configuration mNewOverrideConfig = new Configuration();
// Registered display id as a listener to override config change
private int mDisplayId;
+ private ActivityRecord mConfigActivityRecord;
/** Whether our process is currently running a {@link RecentsAnimation} */
private boolean mRunningRecentsAnimation;
@@ -327,6 +330,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return mDisplayId != INVALID_DISPLAY;
}
+ /** @return {@code true} if the process registered to an activity as a config listener. */
+ @VisibleForTesting
+ boolean registeredForActivityConfigChanges() {
+ return mConfigActivityRecord != null;
+ }
+
void postPendingUiCleanMsg(boolean pendingUiClean) {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
@@ -483,7 +492,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
@Override
protected ConfigurationContainer getParent() {
- return null;
+ // Returning RootWindowContainer as the parent, so that this process controller always
+ // has full configuration and overrides (e.g. from display) are always added on top of
+ // global config.
+ return mAtm.mRootWindowContainer;
}
@HotPath(caller = HotPath.PROCESS_CHANGE)
@@ -507,10 +519,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
return;
}
mActivities.add(r);
+ updateActivityConfigurationListener();
}
void removeActivity(ActivityRecord r) {
mActivities.remove(r);
+ updateActivityConfigurationListener();
}
void makeFinishingForProcessRemoved() {
@@ -521,6 +535,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
void clearActivities() {
mActivities.clear();
+ updateActivityConfigurationListener();
}
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
@@ -964,19 +979,20 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mAtm.mH.sendMessage(m);
}
- void registerDisplayConfigurationListenerLocked(DisplayContent displayContent) {
+ void registerDisplayConfigurationListener(DisplayContent displayContent) {
if (displayContent == null) {
return;
}
- // A process can only register to one display to listener to the override configuration
+ // A process can only register to one display to listen to the override configuration
// change. Unregister existing listener if it has one before register the new one.
- unregisterDisplayConfigurationListenerLocked();
+ unregisterDisplayConfigurationListener();
+ unregisterActivityConfigurationListener();
mDisplayId = displayContent.mDisplayId;
displayContent.registerConfigurationChangeListener(this);
}
@VisibleForTesting
- void unregisterDisplayConfigurationListenerLocked() {
+ void unregisterDisplayConfigurationListener() {
if (mDisplayId == INVALID_DISPLAY) {
return;
}
@@ -986,6 +1002,48 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
displayContent.unregisterConfigurationChangeListener(this);
}
mDisplayId = INVALID_DISPLAY;
+ onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+ }
+
+ private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
+ if (activityRecord == null) {
+ return;
+ }
+ // A process can only register to one activityRecord to listen to the override configuration
+ // change. Unregister existing listener if it has one before register the new one.
+ unregisterDisplayConfigurationListener();
+ unregisterActivityConfigurationListener();
+ mConfigActivityRecord = activityRecord;
+ activityRecord.registerConfigurationChangeListener(this);
+ }
+
+ private void unregisterActivityConfigurationListener() {
+ if (mConfigActivityRecord == null) {
+ return;
+ }
+ mConfigActivityRecord.unregisterConfigurationChangeListener(this);
+ mConfigActivityRecord = null;
+ onMergedOverrideConfigurationChanged(Configuration.EMPTY);
+ }
+
+ /**
+ * Check if activity configuration override for the activity process needs an update and perform
+ * if needed. By default we try to override the process configuration to match the top activity
+ * config to increase app compatibility with multi-window and multi-display. The process will
+ * always track the configuration of the non-finishing activity last added to the process.
+ */
+ private void updateActivityConfigurationListener() {
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord activityRecord = mActivities.get(i);
+ if (!activityRecord.finishing && !activityRecord.containsListener(this)) {
+ // Eligible activity is found, update listener.
+ registerActivityConfigurationListener(activityRecord);
+ return;
+ }
+ }
+
+ // No eligible activities found, let's remove the configuration listener.
+ unregisterActivityConfigurationListener();
}
@Override
@@ -995,8 +1053,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
}
@Override
- public void onRequestedOverrideConfigurationChanged(Configuration newOverrideConfig) {
- super.onRequestedOverrideConfigurationChanged(newOverrideConfig);
+ public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
+ // Make sure that we don't accidentally override the activity type.
+ mNewOverrideConfig.setTo(mergedOverrideConfig);
+ mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
updateConfiguration();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 36e9273a0904..a429741607e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3402,7 +3402,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
"Reporting new frame to %s: %s", this,
mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
- new MergedConfiguration(mWmService.mRoot.getConfiguration(),
+ new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration());
setLastReportedMergedConfiguration(mergedConfiguration);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b8b0dbf9157f..14bd72b0fd21 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7983,12 +7983,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
if (!mHasFeature) {
return false;
}
- if (parent) {
- ActiveAdmin ap = getActiveAdminForCallerLocked(who,
- DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
- enforceProfileOwnerOfOrganizationOwnedDevice(ap);
- }
synchronized (getLockObject()) {
+ if (parent) {
+ final ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA, parent);
+ enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+ }
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
return (admin != null) ? admin.disableCamera : false;
@@ -15051,4 +15051,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
Slog.d(LOG_TAG, message);
}
}
+
+ @Override
+ public void setCommonCriteriaModeEnabled(ComponentName admin, boolean enabled) {
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ }
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.settingsGlobalPutInt(Settings.Global.COMMON_CRITERIA_MODE,
+ enabled ? 1 : 0));
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.SET_COMMON_CRITERIA_MODE)
+ .setAdmin(admin)
+ .setBoolean(enabled)
+ .write();
+ }
+
+ @Override
+ public boolean isCommonCriteriaModeEnabled(ComponentName admin) {
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(admin,
+ DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ }
+ return mInjector.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0) != 0;
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 109a6fd528b8..92bdba03fa09 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -223,8 +223,14 @@ public final class SystemServer {
"com.android.server.usb.UsbService$Lifecycle";
private static final String MIDI_SERVICE_CLASS =
"com.android.server.midi.MidiService$Lifecycle";
+ private static final String WIFI_APEX_SERVICE_JAR_PATH =
+ "/apex/com.android.wifi/javalib/wifi-service.jar";
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
+ private static final String WIFI_SCANNING_SERVICE_CLASS =
+ "com.android.server.wifi.scanner.WifiScanningService";
+ private static final String WIFI_RTT_SERVICE_CLASS =
+ "com.android.server.wifi.rtt.RttService";
private static final String WIFI_AWARE_SERVICE_CLASS =
"com.android.server.wifi.aware.WifiAwareService";
private static final String WIFI_P2P_SERVICE_CLASS =
@@ -1426,33 +1432,36 @@ public final class SystemServer {
PackageManager.FEATURE_WIFI)) {
// Wifi Service must be started first for wifi-related services.
t.traceBegin("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
t.traceBegin("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT)) {
t.traceBegin("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_RTT_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_AWARE)) {
t.traceBegin("StartWifiAware");
- mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_AWARE_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_DIRECT)) {
t.traceBegin("StartWifiP2P");
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
+ mSystemServiceManager.startServiceFromJar(
+ WIFI_P2P_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH);
t.traceEnd();
}
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 9d384e90d253..ef0ca66610c9 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -44,8 +44,10 @@ android_robolectric_test {
// Include the testing libraries
libs: [
+ "mockito-robolectric-prebuilt",
"platform-test-annotations",
"testng",
+ "truth-prebuilt",
],
instrumentation_for: "BackupFrameworksServicesLib",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
new file mode 100644
index 000000000000..09466e7dd668
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationConnectionWrapperTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.server.accessibility.magnification;
+
+
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.view.Display;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for WindowMagnificationConnectionWrapper. We don't test {@code
+ * WindowMagnificationConnectionWrapper#linkToDeath(IBinder.DeathRecipient)} since it's tested in
+ * {@link WindowMagnificationManagerTest}.
+ */
+public class WindowMagnificationConnectionWrapperTest {
+
+ private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
+
+ @Mock
+ private IWindowMagnificationConnection mConnection;
+ @Mock
+ private IWindowMagnificationConnectionCallback mCallback;
+ private WindowMagnificationConnectionWrapper mConnectionWrapper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mConnectionWrapper = new WindowMagnificationConnectionWrapper(mConnection);
+ }
+
+ @Test
+ public void enableWindowMagnification() throws RemoteException {
+ mConnectionWrapper.enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+ verify(mConnection).enableWindowMagnification(TEST_DISPLAY, 2, 100f, 200f);
+ }
+
+ @Test
+ public void setScale() throws RemoteException {
+ mConnectionWrapper.setScale(TEST_DISPLAY, 3.0f);
+ verify(mConnection).setScale(TEST_DISPLAY, 3.0f);
+ }
+
+ @Test
+ public void disableWindowMagnification() throws RemoteException {
+ mConnectionWrapper.disableWindowMagnification(TEST_DISPLAY);
+ verify(mConnection).disableWindowMagnification(TEST_DISPLAY);
+ }
+
+ @Test
+ public void moveWindowMagnifier() throws RemoteException {
+ mConnectionWrapper.moveWindowMagnifier(0, 100, 150);
+ verify(mConnection).moveWindowMagnifier(0, 100, 150);
+ }
+
+ @Test
+ public void setMirrorWindowCallback() throws RemoteException {
+ mConnectionWrapper.setConnectionCallback(mCallback);
+ verify(mConnection).setConnectionCallback(mCallback);
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
new file mode 100644
index 000000000000..780a6c08687a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.server.accessibility.magnification;
+
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.accessibility.IWindowMagnificationConnection;
+import android.view.accessibility.IWindowMagnificationConnectionCallback;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for WindowMagnificationManager.
+ */
+public class WindowMagnificationManagerTest {
+
+ private MockWindowMagnificationConnection mMockConnection;
+ private WindowMagnificationManager mWindowMagnificationManager;
+
+ @Before
+ public void setUp() {
+ mMockConnection = new MockWindowMagnificationConnection();
+ mWindowMagnificationManager = new WindowMagnificationManager();
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_wrapperIsNullAndLinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ @Test
+ public void setConnection_connectionIsNull_setMirrorWindowCallbackAndHasWrapper()
+ throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0));
+ verify(mMockConnection.getConnection()).setConnectionCallback(
+ any(IWindowMagnificationConnectionCallback.class));
+ }
+
+ @Test
+ public void binderDied_hasConnection_wrapperIsNullAndUnlinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(),
+ 0);
+ }
+
+ /**
+ * This test simulates {@link WindowMagnificationManager#setConnection} is called by thread A
+ * and then the former connection is called by thread B. In this situation we should keep the
+ * new connection.
+ */
+ @Test
+ public void
+ setSecondConnectionAndFormerConnectionBinderDead_hasWrapperAndNotCallUnlinkToDeath() {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+ MockWindowMagnificationConnection secondConnection =
+ new MockWindowMagnificationConnection();
+
+ mWindowMagnificationManager.setConnection(secondConnection.getConnection());
+ mMockConnection.getDeathRecipient().binderDied();
+
+ assertNotNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.asBinder()).unlinkToDeath(mMockConnection.getDeathRecipient(), 0);
+ verify(secondConnection.asBinder(), never()).unlinkToDeath(
+ secondConnection.getDeathRecipient(), 0);
+ }
+
+ @Test
+ public void setNullConnection_hasConnection_wrapperIsNull() throws RemoteException {
+ mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
+
+ mWindowMagnificationManager.setConnection(null);
+
+ assertNull(mWindowMagnificationManager.mConnectionWrapper);
+ verify(mMockConnection.getConnection()).setConnectionCallback(null);
+ }
+
+ private static class MockWindowMagnificationConnection {
+
+ private final IWindowMagnificationConnection mConnection;
+ private final Binder mBinder;
+ private IBinder.DeathRecipient mDeathRecipient;
+
+ MockWindowMagnificationConnection() {
+ mConnection = mock(IWindowMagnificationConnection.class);
+ mBinder = mock(Binder.class);
+ when(mConnection.asBinder()).thenReturn(mBinder);
+ doAnswer((invocation) -> {
+ mDeathRecipient = invocation.getArgument(0);
+ return null;
+ }).when(mBinder).linkToDeath(
+ any(IBinder.DeathRecipient.class), eq(0));
+ }
+
+ IWindowMagnificationConnection getConnection() {
+ return mConnection;
+ }
+
+ public IBinder.DeathRecipient getDeathRecipient() {
+ return mDeathRecipient;
+ }
+
+ Binder asBinder() {
+ return mBinder;
+ }
+ }
+}
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 bfadeea40034..632a2c1edfae 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5721,6 +5721,31 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.getAllCrossProfilePackages());
}
+ public void testSetCommonCriteriaMode_asDeviceOwner() throws Exception {
+ setDeviceOwner();
+
+ dpm.setCommonCriteriaModeEnabled(admin1, true);
+ verify(getServices().settings).settingsGlobalPutInt(
+ Settings.Global.COMMON_CRITERIA_MODE, 1);
+
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
+ .thenReturn(1);
+ assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+ }
+
+ public void testSetCommonCriteriaMode_asPoOfOrgOwnedDevice() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ dpm.setCommonCriteriaModeEnabled(admin1, true);
+ verify(getServices().settings).settingsGlobalPutInt(
+ Settings.Global.COMMON_CRITERIA_MODE, 1);
+
+ when(getServices().settings.settingsGlobalGetInt(Settings.Global.COMMON_CRITERIA_MODE, 0))
+ .thenReturn(1);
+ assertTrue(dpm.isCommonCriteriaModeEnabled(admin1));
+ }
+
private void setCrossProfileAppsList(String... packages) {
when(mContext.getResources()
.getStringArray(eq(R.array.cross_profile_apps)))
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 1f660742122d..9e98427db709 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.google.common.truth.Truth.assertThat;
@@ -25,8 +26,17 @@ import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
+import android.os.IPowerManager;
import android.os.Looper;
+import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.test.TestLooper;
import androidx.test.InstrumentationRegistry;
@@ -36,6 +46,8 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -98,6 +110,7 @@ public class HdmiControlServiceTest {
}
private static final String TAG = "HdmiControlServiceTest";
+ private Context mContextSpy;
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceMyDevice mMyAudioSystemDevice;
@@ -109,15 +122,24 @@ public class HdmiControlServiceTest {
private boolean mStandbyMessageReceived;
private HdmiPortInfo[] mHdmiPortInfo;
+ @Mock private IPowerManager mIPowerManagerMock;
+
@Before
- public void SetUp() {
- mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
- @Override
- boolean isStandbyMessageReceived() {
- return mStandbyMessageReceived;
- }
- };
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+ PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null);
+ when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager);
+ when(mIPowerManagerMock.isInteractive()).thenReturn(true);
+
+ mHdmiControlService = new HdmiControlService(mContextSpy) {
+ @Override
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+ };
mMyLooper = mTestLooper.getLooper();
mMyAudioSystemDevice =
@@ -198,4 +220,33 @@ public class HdmiControlServiceTest {
mHdmiControlService.initPortInfo();
assertThat(mHdmiControlService.pathToPortId(0x1000)).isEqualTo(Constants.INVALID_PORT_ID);
}
+
+ @Test
+ public void initialPowerStatus_normalBoot_isTransientToStandby() {
+ assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+ }
+
+ @Test
+ public void initialPowerStatus_quiescentBoot_isTransientToStandby() throws RemoteException {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ assertThat(mHdmiControlService.getInitialPowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY);
+ }
+
+ @Test
+ public void powerStatusAfterBootComplete_normalBoot_isOn() {
+ mHdmiControlService.setPowerStatus(HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED);
+ assertThat(mHdmiControlService.getPowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON);
+ }
+
+ @Test
+ public void powerStatusAfterBootComplete_quiescentBoot_isStandby() throws RemoteException {
+ when(mIPowerManagerMock.isInteractive()).thenReturn(false);
+ mHdmiControlService.onBootPhase(PHASE_BOOT_COMPLETED);
+ assertThat(mHdmiControlService.getPowerStatus()).isEqualTo(
+ HdmiControlManager.POWER_STATUS_STANDBY);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index 8ee5f5fb9de8..e5cbeee2860d 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -30,6 +30,8 @@ import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BIT
import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
+import static com.android.server.integrity.serializer.RuleBinarySerializer.INDEXED_RULE_SIZE_LIMIT;
+import static com.android.server.integrity.serializer.RuleBinarySerializer.NONINDEXED_RULE_SIZE_LIMIT;
import static com.android.server.integrity.utils.TestUtils.getBits;
import static com.android.server.integrity.utils.TestUtils.getBytes;
import static com.android.server.integrity.utils.TestUtils.getValueBits;
@@ -112,8 +114,7 @@ public class RuleBinarySerializerTest {
assertExpectException(
RuleSerializeException.class,
- /* expectedExceptionMessageRegex= */ "Index buckets cannot be created for null"
- + " rule list.",
+ /* expectedExceptionMessageRegex= */ "Null rules cannot be serialized.",
() -> binarySerializer.serialize(null, /* formatVersion= */ Optional.empty()));
}
@@ -203,8 +204,8 @@ public class RuleBinarySerializerTest {
+ getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+ SERIALIZED_END_INDEXING_KEY
+ getBits(
- DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
- /* numOfBits= */ 32);
+ DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length,
+ /* numOfBits= */ 32);
expectedIndexingOutputStream.write(
getBytes(
expectedIndexingBitsForIndexed
@@ -618,6 +619,131 @@ public class RuleBinarySerializerTest {
.isEqualTo(expectedIndexingOutputStream.toByteArray());
}
+ @Test
+ public void testBinaryString_totalRuleSizeLimitReached() {
+ int ruleCount = INDEXED_RULE_SIZE_LIMIT - 1;
+ String packagePrefix = "package.name.";
+ String appCertificatePrefix = "app.cert.";
+ String installerNamePrefix = "installer.";
+
+ // Create the rule set with more rules than the system can handle in total.
+ List<Rule> ruleList = new ArrayList();
+ for (int count = 0; count < ruleCount; count++) {
+ ruleList.add(
+ getRuleWithPackageNameAndSampleInstallerName(
+ String.format("%s%04d", packagePrefix, count)));
+ }
+ for (int count = 0; count < ruleCount; count++) {
+ ruleList.add(
+ getRuleWithAppCertificateAndSampleInstallerName(
+ String.format("%s%04d", appCertificatePrefix, count)));
+ }
+ for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT - 1; count++) {
+ ruleList.add(
+ getNonIndexedRuleWithInstallerName(
+ String.format("%s%04d", installerNamePrefix, count)));
+ }
+
+ // Serialize the rules.
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ "Too many rules provided",
+ () ->
+ binarySerializer.serialize(
+ ruleList,
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream));
+ }
+
+ @Test
+ public void testBinaryString_tooManyPackageNameIndexedRules() {
+ String packagePrefix = "package.name.";
+
+ // Create a rule set with too many package name indexed rules.
+ List<Rule> ruleList = new ArrayList();
+ for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
+ ruleList.add(
+ getRuleWithPackageNameAndSampleInstallerName(
+ String.format("%s%04d", packagePrefix, count)));
+ }
+
+ // Serialize the rules.
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ "Too many rules provided in the indexing group.",
+ () ->
+ binarySerializer.serialize(
+ ruleList,
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream));
+ }
+
+ @Test
+ public void testBinaryString_tooManyAppCertificateIndexedRules() {
+ String appCertificatePrefix = "app.cert.";
+
+ // Create a rule set with too many app certificate indexed rules.
+ List<Rule> ruleList = new ArrayList();
+ for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) {
+ ruleList.add(
+ getRuleWithAppCertificateAndSampleInstallerName(
+ String.format("%s%04d", appCertificatePrefix, count)));
+ }
+
+ // Serialize the rules.
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ "Too many rules provided in the indexing group.",
+ () ->
+ binarySerializer.serialize(
+ ruleList,
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream));
+ }
+
+ @Test
+ public void testBinaryString_tooManyNonIndexedRules() {
+ String installerNamePrefix = "installer.";
+
+ // Create a rule set with too many unindexed rules.
+ List<Rule> ruleList = new ArrayList();
+ for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT + 1; count++) {
+ ruleList.add(
+ getNonIndexedRuleWithInstallerName(
+ String.format("%s%04d", installerNamePrefix, count)));
+ }
+
+ // Serialize the rules.
+ ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ "Too many rules provided in the indexing group.",
+ () ->
+ binarySerializer.serialize(
+ ruleList,
+ /* formatVersion= */ Optional.empty(),
+ ruleOutputStream,
+ indexingOutputStream));
+ }
+
private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) {
return new Rule(
new CompoundFormula(
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 13643a09daaa..25cef56cd21e 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
@@ -754,4 +755,55 @@ public class PowerManagerServiceTest {
SystemClock.sleep(11);
assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
}
+
+ @Test
+ public void testBoot_ShouldBeAwake() throws Exception {
+ createService();
+ startSystem();
+
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE);
+ verify(mNotifierMock, never()).onWakefulnessChangeStarted(anyInt(), anyInt(), anyLong());
+ }
+
+ @Test
+ public void testBoot_DesiredScreenPolicyShouldBeBright() throws Exception {
+ createService();
+ startSystem();
+
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_BRIGHT);
+ }
+
+ @Test
+ public void testQuiescentBoot_ShouldBeAsleep() throws Exception {
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+ createService();
+ startSystem();
+
+ assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP);
+ verify(mNotifierMock).onWakefulnessChangeStarted(eq(WAKEFULNESS_ASLEEP), anyInt(),
+ anyLong());
+ }
+
+ @Test
+ public void testQuiescentBoot_DesiredScreenPolicyShouldBeOff() throws Exception {
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+ createService();
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_OFF);
+
+ startSystem();
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_OFF);
+ }
+
+ @Test
+ public void testQuiescentBoot_WakeUp_DesiredScreenPolicyShouldBeBright() throws Exception {
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+ createService();
+ startSystem();
+ forceAwake();
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_BRIGHT);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
index f5af3ec76788..d16c232afea9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -18,6 +18,7 @@ package com.android.server.notification;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
@@ -30,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationChannel;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.notification.StatusBarNotification;
import com.android.server.UiServiceTestCase;
@@ -52,6 +54,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase {
public void testExtractsUpdatedChannel() {
NotificationChannelExtractor extractor = new NotificationChannelExtractor();
extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
final Notification.Builder builder = new Notification.Builder(getContext())
@@ -71,4 +74,62 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase {
assertNull(extractor.process(r));
assertEquals(updatedChannel, r.getChannel());
}
+
+ @Test
+ public void testInvalidShortcutFlagEnabled_looksUpCorrectChannel() {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+
+ NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setStyle(new Notification.MessagingStyle("name"))
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+ NotificationChannel updatedChannel =
+ new NotificationChannel("a", "", IMPORTANCE_HIGH);
+ when(mConfig.getConversationNotificationChannel(
+ any(), anyInt(), eq("a"), eq(r.sbn.getShortcutId(mContext)), eq(true), eq(false)))
+ .thenReturn(updatedChannel);
+
+ assertNull(extractor.process(r));
+ assertEquals(updatedChannel, r.getChannel());
+ }
+
+ @Test
+ public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+
+ NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+ extractor.setConfig(mConfig);
+ extractor.initialize(mContext, null);
+
+ NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+ final Notification.Builder builder = new Notification.Builder(getContext())
+ .setContentTitle("foo")
+ .setStyle(new Notification.MessagingStyle("name"))
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ Notification n = builder.build();
+ StatusBarNotification sbn = new StatusBarNotification("", "", 0, "tag", 0,
+ 0, n, UserHandle.ALL, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+ NotificationChannel updatedChannel =
+ new NotificationChannel("a", "", IMPORTANCE_HIGH);
+ when(mConfig.getConversationNotificationChannel(
+ any(), anyInt(), eq("a"), eq(null), eq(true), eq(false)))
+ .thenReturn(updatedChannel);
+
+ assertNull(extractor.process(r));
+ assertEquals(updatedChannel, r.getChannel());
+ }
}
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 2ac464251c73..c6c64c9e0f6b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5834,7 +5834,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mBinderService.createNotificationChannels(PKG, new ParceledListSlice(Arrays.asList(
orig)));
- mBinderService.createConversationNotificationChannelForPackage(PKG, mUid, orig, "friend");
+ mBinderService.createConversationNotificationChannelForPackage(
+ PKG, mUid, "key", orig, "friend");
NotificationChannel friendChannel = mBinderService.getConversationNotificationChannel(
PKG, 0, PKG, original.getId(), false, "friend");
@@ -5869,9 +5870,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
String conversationId = "friend";
mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, NotificationChannel.CREATOR.createFromParcel(msgParcel), conversationId);
+ PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(msgParcel),
+ conversationId);
mBinderService.createConversationNotificationChannelForPackage(
- PKG, mUid, NotificationChannel.CREATOR.createFromParcel(callParcel),
+ PKG, mUid, "key", NotificationChannel.CREATOR.createFromParcel(callParcel),
conversationId);
NotificationChannel messagesChild = mBinderService.getConversationNotificationChannel(
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 6f1657441fb5..c1c74da50c7c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
+import static android.util.FeatureFlagUtils.NOTIF_CONVO_BYPASS_SHORTCUT_REQ;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
@@ -2858,4 +2859,84 @@ public class PreferencesHelperTest extends UiServiceTestCase {
// good
}
}
+
+ @Test
+ public void testPlaceholderConversationId_flagOn() throws Exception {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "true");
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+ }
+
+ @Test
+ public void testPlaceholderConversationId_flagOff() throws Exception {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+ }
+
+ @Test
+ public void testNormalConversationId_flagOff() throws Exception {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+ }
+
+ @Test
+ public void testNoConversationId_flagOff() throws Exception {
+ Settings.Global.putString(
+ mContext.getContentResolver(), NOTIF_CONVO_BYPASS_SHORTCUT_REQ, "false");
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+
+ final String xml = "<ranking version=\"1\">\n"
+ + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n"
+ + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
+ + "</package>"
+ + "</ranking>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
+ null);
+ parser.nextTag();
+ mHelper.readXml(parser, false, UserHandle.USER_ALL);
+
+ assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
+ }
}
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 a3e94599cad3..ad63d078fa67 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -56,6 +58,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -1175,4 +1178,160 @@ public class ActivityRecordTests extends ActivityTestsBase {
verify(mActivity).removeFromHistory(anyString());
}
+
+ @Test
+ public void testActivityOverridesProcessConfig() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertFalse(wpc.registeredForDisplayConfigChanges());
+
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(mActivity.getConfiguration(),
+ secondaryDisplayActivity.getConfiguration());
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final Task firstTaskRecord = mActivity.getTask();
+ final ActivityRecord secondActivityRecord =
+ new ActivityBuilder(mService).setTask(firstTaskRecord).setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities_SecondaryDisplay() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ new ActivityBuilder(mService).setTask(mTask).setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOverridesProcessConfig_TwoActivities_DifferentTasks() {
+ final WindowProcessController wpc = mActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityOnDifferentDisplayUpdatesProcessOverride() {
+ final ActivityRecord secondaryDisplayActivity =
+ createActivityOnDisplay(false /* defaultDisplay */, null /* process */);
+ final WindowProcessController wpc = secondaryDisplayActivity.app;
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ final ActivityRecord secondActivityRecord =
+ createActivityOnDisplay(true /* defaultDisplay */, wpc);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivityRecord.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertFalse(wpc.registeredForDisplayConfigChanges());
+ }
+
+ @Test
+ public void testActivityReparentChangesProcessOverride() {
+ final WindowProcessController wpc = mActivity.app;
+ final Task initialTask = mActivity.getTask();
+ final Configuration initialConf =
+ new Configuration(mActivity.getMergedOverrideConfiguration());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertTrue(wpc.registeredForActivityConfigChanges());
+
+ // Create a new task with custom config to reparent the activity to.
+ final Task newTask =
+ new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ final Configuration newConfig = newTask.getConfiguration();
+ newConfig.densityDpi += 100;
+ newTask.onRequestedOverrideConfigurationChanged(newConfig);
+ assertEquals(newTask.getConfiguration().densityDpi, newConfig.densityDpi);
+
+ // Reparent the activity and verify that config override changed.
+ mActivity.reparent(newTask, 0 /* top */, "test");
+ assertEquals(mActivity.getConfiguration().densityDpi, newConfig.densityDpi);
+ assertEquals(mActivity.getMergedOverrideConfiguration().densityDpi, newConfig.densityDpi);
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+ assertEquals(0, mActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ }
+
+ @Test
+ public void testActivityReparentDoesntClearProcessOverride_TwoActivities() {
+ final WindowProcessController wpc = mActivity.app;
+ final Configuration initialConf =
+ new Configuration(mActivity.getMergedOverrideConfiguration());
+ final Task initialTask = mActivity.getTask();
+ final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(initialTask)
+ .setUseProcess(wpc).build();
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+
+ // Create a new task with custom config to reparent the second activity to.
+ final Task newTask =
+ new TaskBuilder(mSupervisor).setStack(initialTask.getStack()).build();
+ final Configuration newConfig = newTask.getConfiguration();
+ newConfig.densityDpi += 100;
+ newTask.onRequestedOverrideConfigurationChanged(newConfig);
+
+ // Reparent the activity and verify that config override changed.
+ secondActivity.reparent(newTask, 0 /* top */, "test");
+
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+
+ // Reparent the first activity and verify that config override didn't change.
+ mActivity.reparent(newTask, 1 /* top */, "test");
+ assertTrue(wpc.registeredForActivityConfigChanges());
+ assertEquals(0, secondActivity.getMergedOverrideConfiguration()
+ .diff(wpc.getRequestedOverrideConfiguration()));
+ assertNotEquals(initialConf, wpc.getRequestedOverrideConfiguration());
+ }
+
+ /**
+ * Creates an activity on display. For non-default display request it will also create a new
+ * display with custom DisplayInfo.
+ */
+ private ActivityRecord createActivityOnDisplay(boolean defaultDisplay,
+ WindowProcessController process) {
+ final DisplayContent display;
+ if (defaultDisplay) {
+ display = mRootWindowContainer.getDefaultDisplay();
+ } else {
+ display = new TestDisplayContent.Builder(mService, 2000, 1000).setDensityDpi(300)
+ .setPosition(DisplayContent.POSITION_TOP).build();
+ }
+ final ActivityStack stack = display.createStack(WINDOWING_MODE_UNDEFINED,
+ ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final Task task = new TaskBuilder(mSupervisor).setStack(stack).build();
+ return new ActivityBuilder(mService).setTask(task).setUseProcess(process).build();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 0f227246b468..4beede93aea2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -111,6 +111,7 @@ class ActivityTestsBase extends SystemServiceTestsBase {
private int mConfigChanges;
private int mLaunchedFromPid;
private int mLaunchedFromUid;
+ private WindowProcessController mWpc;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -201,6 +202,11 @@ class ActivityTestsBase extends SystemServiceTestsBase {
return this;
}
+ ActivityBuilder setUseProcess(WindowProcessController wpc) {
+ mWpc = wpc;
+ return this;
+ }
+
ActivityRecord build() {
try {
mService.deferWindowLayout();
@@ -263,10 +269,16 @@ class ActivityTestsBase extends SystemServiceTestsBase {
activity.setVisible(true);
}
- final WindowProcessController wpc = new WindowProcessController(mService,
- mService.mContext.getApplicationInfo(), mProcessName, mUid,
- UserHandle.getUserId(12345), mock(Object.class),
- mock(WindowProcessListener.class));
+ final WindowProcessController wpc;
+ if (mWpc != null) {
+ wpc = mWpc;
+ } else {
+ wpc = new WindowProcessController(mService,
+ mService.mContext.getApplicationInfo(), mProcessName, mUid,
+ UserHandle.getUserId(12345), mock(Object.class),
+ mock(WindowProcessListener.class));
+ wpc.setThread(mock(IApplicationThread.class));
+ }
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
doReturn(wpc).when(mService).getProcessController(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 421a458256af..db4fdc77064b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
import android.platform.test.annotations.Presubmit;
import org.junit.Before;
@@ -62,33 +63,33 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
// Register to display 1 as a listener.
TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent1);
assertTrue(testDisplayContent1.containsListener(mWpc));
assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
// Move to display 2.
TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent2);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent2);
assertFalse(testDisplayContent1.containsListener(mWpc));
assertTrue(testDisplayContent2.containsListener(mWpc));
assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
// Null DisplayContent will not change anything.
- mWpc.registerDisplayConfigurationListenerLocked(null);
+ mWpc.registerDisplayConfigurationListener(null);
assertTrue(testDisplayContent2.containsListener(mWpc));
assertEquals(testDisplayContent2.mDisplayId, mWpc.getDisplayId());
// Unregister listener will remove the wpc from registered displays.
- mWpc.unregisterDisplayConfigurationListenerLocked();
+ mWpc.unregisterDisplayConfigurationListener();
assertFalse(testDisplayContent1.containsListener(mWpc));
assertFalse(testDisplayContent2.containsListener(mWpc));
assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
// Unregistration still work even if the display was removed.
- mWpc.registerDisplayConfigurationListenerLocked(testDisplayContent1);
+ mWpc.registerDisplayConfigurationListener(testDisplayContent1);
assertEquals(testDisplayContent1.mDisplayId, mWpc.getDisplayId());
mRootWindowContainer.removeChild(testDisplayContent1);
- mWpc.unregisterDisplayConfigurationListenerLocked();
+ mWpc.unregisterDisplayConfigurationListener();
assertEquals(INVALID_DISPLAY, mWpc.getDisplayId());
}
@@ -129,6 +130,24 @@ public class WindowProcessControllerTests extends ActivityTestsBase {
orderVerifier.verifyNoMoreInteractions();
}
+ @Test
+ public void testConfigurationForSecondaryScreen() {
+ final WindowProcessController wpc = new WindowProcessController(
+ mService, mock(ApplicationInfo.class), null, 0, -1, null, null);
+ // By default, the process should not listen to any display.
+ assertEquals(INVALID_DISPLAY, wpc.getDisplayId());
+
+ // Register to a new display as a listener.
+ final DisplayContent display = new TestDisplayContent.Builder(mService, 2000, 1000)
+ .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
+ wpc.registerDisplayConfigurationListener(display);
+
+ assertEquals(display.mDisplayId, wpc.getDisplayId());
+ final Configuration expectedConfig = mService.mRootWindowContainer.getConfiguration();
+ expectedConfig.updateFrom(display.getConfiguration());
+ assertEquals(expectedConfig, wpc.getConfiguration());
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mService, 1000, 1500).build();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 56ca8c7790cb..c4bcc170ffc3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3859,21 +3859,20 @@ public class TelephonyManager {
}
/**
- * Return if the current radio is LTE on CDMA. This is a tri-state return value as for a period
- * of time the mode may be unknown.
+ * Return if the current radio has global mode enabled, meaning it supports
+ * both 3GPP and 3GPP2 radio technologies at the same time.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
- *
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
+ * @return {@code true} if global mode is enabled
+ * {@code false} if global mode is not enabled or unknown
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage
- public int getLteOnCdmaMode() {
- return getLteOnCdmaMode(getSubId());
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isGlobalModeEnabled() {
+ return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
}
/**
@@ -3886,7 +3885,7 @@ public class TelephonyManager {
* or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public int getLteOnCdmaMode(int subId) {
try {
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 9b739d3acbbc..8c11df41cf64 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -27,6 +27,7 @@ import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.os.Binder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
@@ -383,6 +384,8 @@ public class ProvisioningManager {
callback.setExecutor(executor);
try {
getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
+ } catch (ServiceSpecificException e) {
+ throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 11d5b250d752..1c6920986318 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -74,6 +75,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
@@ -206,13 +208,11 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
}
public void suspend() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void resume() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void disconnect() {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
index 9c4f4606a9bc..fd236107bc6e 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
+++ b/wifi/java/android/net/wifi/IScoreChangeCallback.aidl
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-package android.media.tv.tuner.frontend;
+package android.net.wifi;
/**
- * Frontend Callback.
+ * Interface for Wi-Fi network score callback.
*
* @hide
*/
-public interface FrontendCallback {
+oneway interface IScoreChangeCallback
+{
+ void onStatusChange(int sessionId, boolean exiting);
- /**
- * Invoked when there is a frontend event.
- */
- void onEvent(int frontendEventType);
+ void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
}
diff --git a/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
new file mode 100644
index 000000000000..d9a3b0109a09
--- /dev/null
+++ b/wifi/java/android/net/wifi/IWifiConnectedNetworkScorer.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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 android.net.wifi.IScoreChangeCallback;
+
+/**
+ * Interface for Wi-Fi connected network scorer.
+ *
+ * @hide
+ */
+oneway interface IWifiConnectedNetworkScorer
+{
+ void start(int sessionId);
+
+ void stop(int sessionId);
+
+ void setScoreChangeCallback(IScoreChangeCallback cbImpl);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 67f166327b56..5a98ac86e783 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -35,6 +35,7 @@ import android.net.wifi.ISoftApCallback;
import android.net.wifi.ISuggestionConnectionStatusListener;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
+import android.net.wifi.IWifiConnectedNetworkScorer;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiConfiguration;
@@ -254,4 +255,8 @@ interface IWifiManager
int calculateSignalLevel(int rssi);
List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(in List<ScanResult> scanResults);
+
+ boolean setWifiConnectedNetworkScorer(in IBinder binder, in IWifiConnectedNetworkScorer scorer);
+
+ void clearWifiConnectedNetworkScorer();
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ec3de43ee85a..64d4eafe71bf 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -5806,4 +5806,186 @@ public class WifiManager {
return new SparseArray<>();
}
}
+
+ /**
+ * Callback interface for framework to receive network status changes and trigger of updating
+ * {@link WifiUsabilityStatsEntry}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface ScoreChangeCallback {
+ /**
+ * Called by applications to indicate network status.
+ *
+ * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+ * {@link WifiConnectedNetworkScorer#start(int)}.
+ * @param isUsable The bit to indicate whether current Wi-Fi network is usable or not.
+ * Populated by connected network scorer in applications.
+ */
+ void onStatusChange(int sessionId, boolean isUsable);
+
+ /**
+ * Called by applications to trigger an update of {@link WifiUsabilityStatsEntry}.
+ * To receive update applications need to add WifiUsabilityStatsEntry listener. See
+ * {@link addOnWifiUsabilityStatsListener(Executor, OnWifiUsabilityStatsListener)}.
+ *
+ * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+ * {@link WifiConnectedNetworkScorer#start(int)}.
+ */
+ void onTriggerUpdateOfWifiUsabilityStats(int sessionId);
+ }
+
+ /**
+ * Callback proxy for {@link ScoreChangeCallback} objects.
+ *
+ * @hide
+ */
+ private class ScoreChangeCallbackProxy implements ScoreChangeCallback {
+ private final IScoreChangeCallback mScoreChangeCallback;
+
+ private ScoreChangeCallbackProxy(IScoreChangeCallback callback) {
+ mScoreChangeCallback = callback;
+ }
+
+ @Override
+ public void onStatusChange(int sessionId, boolean isUsable) {
+ try {
+ mScoreChangeCallback.onStatusChange(sessionId, isUsable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onTriggerUpdateOfWifiUsabilityStats(int sessionId) {
+ try {
+ mScoreChangeCallback.onTriggerUpdateOfWifiUsabilityStats(sessionId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Interface for Wi-Fi connected network scorer. Should be implemented by applications and set
+ * when calling
+ * {@link WifiManager#setWifiConnectedNetworkScorer(Executor, WifiConnectedNetworkScorer)}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface WifiConnectedNetworkScorer {
+ /**
+ * Called by framework to indicate the start of a network connection.
+ * @param sessionId The ID to indicate current Wi-Fi network connection.
+ */
+ void start(int sessionId);
+
+ /**
+ * Called by framework to indicate the end of a network connection.
+ * @param sessionId The ID to indicate current Wi-Fi network connection obtained from
+ * {@link WifiConnectedNetworkScorer#start(int)}.
+ */
+ void stop(int sessionId);
+
+ /**
+ * Framework sets callback for score change events after application sets its scorer.
+ * @param cbImpl The instance for {@link WifiManager#ScoreChangeCallback}. Should be
+ * implemented and instantiated by framework.
+ */
+ void setScoreChangeCallback(@NonNull ScoreChangeCallback cbImpl);
+ }
+
+ /**
+ * Callback proxy for {@link WifiConnectedNetworkScorer} objects.
+ *
+ * @hide
+ */
+ private class WifiConnectedNetworkScorerProxy extends IWifiConnectedNetworkScorer.Stub {
+ private Executor mExecutor;
+ private WifiConnectedNetworkScorer mScorer;
+
+ WifiConnectedNetworkScorerProxy(Executor executor, WifiConnectedNetworkScorer scorer) {
+ mExecutor = executor;
+ mScorer = scorer;
+ }
+
+ @Override
+ public void start(int sessionId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "WifiConnectedNetworkScorer: " + "start: sessionId=" + sessionId);
+ }
+ Binder.clearCallingIdentity();
+ mExecutor.execute(() -> mScorer.start(sessionId));
+ }
+
+ @Override
+ public void stop(int sessionId) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "WifiConnectedNetworkScorer: " + "stop: sessionId=" + sessionId);
+ }
+ Binder.clearCallingIdentity();
+ mExecutor.execute(() -> mScorer.stop(sessionId));
+ }
+
+ @Override
+ public void setScoreChangeCallback(IScoreChangeCallback cbImpl) {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "WifiConnectedNetworkScorer: "
+ + "setScoreChangeCallback: cbImpl=" + cbImpl);
+ }
+ Binder.clearCallingIdentity();
+ mExecutor.execute(() -> mScorer.setScoreChangeCallback(
+ new ScoreChangeCallbackProxy(cbImpl)));
+ }
+ }
+
+ /**
+ * Set a callback for Wi-Fi connected network scorer. See {@link WifiConnectedNetworkScorer}.
+ * Only a single scorer can be set. Caller will be invoked periodically by framework to inform
+ * client about start and stop of Wi-Fi connection. Caller can clear a previously set scorer
+ * using {@link clearWifiConnectedNetworkScorer()}.
+ *
+ * @param executor The executor on which callback will be invoked.
+ * @param scorer Scorer for Wi-Fi network implemented by application.
+ * @return true Scorer is set successfully.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+ public boolean setWifiConnectedNetworkScorer(@NonNull @CallbackExecutor Executor executor,
+ @NonNull WifiConnectedNetworkScorer scorer) {
+ if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+ if (scorer == null) throw new IllegalArgumentException("scorer cannot be null");
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "setWifiConnectedNetworkScorer: scorer=" + scorer);
+ }
+ try {
+ return mService.setWifiConnectedNetworkScorer(new Binder(),
+ new WifiConnectedNetworkScorerProxy(executor, scorer));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allow caller to clear a previously set scorer. After calling this method,
+ * client will no longer receive information about start and stop of Wi-Fi connection.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
+ public void clearWifiConnectedNetworkScorer() {
+ if (mVerboseLoggingEnabled) {
+ Log.v(TAG, "clearWifiConnectedNetworkScorer");
+ }
+ try {
+ mService.clearWifiConnectedNetworkScorer();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java
new file mode 100644
index 000000000000..de1c7600f8ef
--- /dev/null
+++ b/wifi/java/android/net/wifi/wificond/DeviceWiphyCapabilities.java
@@ -0,0 +1,160 @@
+/*
+ * 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.wificond;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.wifi.ScanResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * DeviceWiphyCapabilities for wificond
+ *
+ * @hide
+ */
+@SystemApi
+public final class DeviceWiphyCapabilities implements Parcelable {
+ private static final String TAG = "DeviceWiphyCapabilities";
+
+ private boolean m80211nSupported;
+ private boolean m80211acSupported;
+ private boolean m80211axSupported;
+
+ /** public constructor */
+ public DeviceWiphyCapabilities() {
+ m80211nSupported = false;
+ m80211acSupported = false;
+ m80211axSupported = false;
+ }
+
+ /**
+ * Get the IEEE 802.11 standard support
+ *
+ * @param standard the IEEE 802.11 standard to check on its support.
+ * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ * @return {@code true} if supported, {@code false} otherwise.
+ */
+ public boolean isWifiStandardSupported(int standard) {
+ switch (standard) {
+ case ScanResult.WIFI_STANDARD_LEGACY:
+ return true;
+ case ScanResult.WIFI_STANDARD_11N:
+ return m80211nSupported;
+ case ScanResult.WIFI_STANDARD_11AC:
+ return m80211acSupported;
+ case ScanResult.WIFI_STANDARD_11AX:
+ return m80211axSupported;
+ default:
+ Log.e(TAG, "isWifiStandardSupported called with invalid standard: " + standard);
+ return false;
+ }
+ }
+
+ /**
+ * Set the IEEE 802.11 standard support
+ *
+ * @param standard the IEEE 802.11 standard to set its support.
+ * valid values from {@link ScanResult}'s {@code WIFI_STANDARD_}
+ * @param support {@code true} if supported, {@code false} otherwise.
+ */
+ public void setWifiStandardSupport(int standard, boolean support) {
+ switch (standard) {
+ case ScanResult.WIFI_STANDARD_11N:
+ m80211nSupported = support;
+ break;
+ case ScanResult.WIFI_STANDARD_11AC:
+ m80211acSupported = support;
+ break;
+ case ScanResult.WIFI_STANDARD_11AX:
+ m80211axSupported = support;
+ break;
+ default:
+ Log.e(TAG, "setWifiStandardSupport called with invalid standard: " + standard);
+ }
+ }
+
+ /** override comparator */
+ @Override
+ public boolean equals(Object rhs) {
+ if (this == rhs) return true;
+ if (!(rhs instanceof DeviceWiphyCapabilities)) {
+ return false;
+ }
+ DeviceWiphyCapabilities capa = (DeviceWiphyCapabilities) rhs;
+
+ return m80211nSupported == capa.m80211nSupported
+ && m80211acSupported == capa.m80211acSupported
+ && m80211axSupported == capa.m80211axSupported;
+ }
+
+ /** override hash code */
+ @Override
+ public int hashCode() {
+ return Objects.hash(m80211nSupported, m80211acSupported, m80211axSupported);
+ }
+
+ /** implement Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * implement Parcelable interface
+ * |flags| is ignored.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeBoolean(m80211nSupported);
+ out.writeBoolean(m80211acSupported);
+ out.writeBoolean(m80211axSupported);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("m80211nSupported:").append(m80211nSupported ? "Yes" : "No");
+ sb.append("m80211acSupported:").append(m80211acSupported ? "Yes" : "No");
+ sb.append("m80211axSupported:").append(m80211axSupported ? "Yes" : "No");
+ return sb.toString();
+ }
+
+ /** implement Parcelable interface */
+ public static final @NonNull Parcelable.Creator<DeviceWiphyCapabilities> CREATOR =
+ new Parcelable.Creator<DeviceWiphyCapabilities>() {
+ /**
+ * Caller is responsible for providing a valid parcel.
+ */
+ @Override
+ public DeviceWiphyCapabilities createFromParcel(Parcel in) {
+ DeviceWiphyCapabilities capabilities = new DeviceWiphyCapabilities();
+ capabilities.m80211nSupported = in.readBoolean();
+ capabilities.m80211acSupported = in.readBoolean();
+ capabilities.m80211axSupported = in.readBoolean();
+ return capabilities;
+ }
+
+ @Override
+ public DeviceWiphyCapabilities[] newArray(int size) {
+ return new DeviceWiphyCapabilities[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index f70bdac25c33..4847640b1418 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -1052,6 +1052,22 @@ public class WifiCondManager {
}
/**
+ * Get the device phy capabilities for a given interface
+ */
+ @Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) {
+ if (mWificond == null) {
+ Log.e(TAG, "Can not query for device wiphy capabilities at this time");
+ return null;
+ }
+
+ try {
+ return mWificond.getDeviceWiphyCapabilities(ifaceName);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Register the provided callback handler for SoftAp events. Note that the Soft AP itself is
* configured using {@link #setupInterfaceForSoftApMode(String)}.
*
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 1cf3825d2397..08822e2762f6 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -32,6 +32,7 @@ import android.net.wifi.ISoftApCallback;
import android.net.wifi.ISuggestionConnectionStatusListener;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
+import android.net.wifi.IWifiConnectedNetworkScorer;
import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
@@ -600,4 +601,15 @@ public class BaseWifiService extends IWifiManager.Stub {
List<ScanResult> scanResults) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public boolean setWifiConnectedNetworkScorer(IBinder binder,
+ IWifiConnectedNetworkScorer scorer) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clearWifiConnectedNetworkScorer() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 983ac8216124..1ee5374aaa69 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -82,6 +82,7 @@ import android.net.wifi.WifiManager.ScanResultsCallback;
import android.net.wifi.WifiManager.SoftApCallback;
import android.net.wifi.WifiManager.SuggestionConnectionStatusListener;
import android.net.wifi.WifiManager.TrafficStateCallback;
+import android.net.wifi.WifiManager.WifiConnectedNetworkScorer;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -138,6 +139,7 @@ public class WifiManagerTest {
@Mock Executor mExecutor;
@Mock Executor mAnotherExecutor;
@Mock ActivityManager mActivityManager;
+ @Mock WifiConnectedNetworkScorer mWifiConnectedNetworkScorer;
private Handler mHandler;
private TestLooper mLooper;
@@ -2230,4 +2232,63 @@ public class WifiManagerTest {
assertEquals(testResults, mWifiManager
.getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(new ArrayList<>()));
}
+
+ /**
+ * Verify the call to setWifiConnectedNetworkScorer goes to WifiServiceImpl.
+ */
+ @Test
+ public void setWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception {
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+ verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+ any(IWifiConnectedNetworkScorer.Stub.class));
+ }
+
+ /**
+ * Verify the call to clearWifiConnectedNetworkScorer goes to WifiServiceImpl.
+ */
+ @Test
+ public void clearWifiConnectedNetworkScorerGoesToWifiServiceImpl() throws Exception {
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+ verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+ any(IWifiConnectedNetworkScorer.Stub.class));
+
+ mWifiManager.clearWifiConnectedNetworkScorer();
+ verify(mWifiService).clearWifiConnectedNetworkScorer();
+ }
+
+ /**
+ * Verify that Wi-Fi connected scorer receives score change callback after registeration.
+ */
+ @Test
+ public void verifyScorerReceiveScoreChangeCallbackAfterRegistration() throws Exception {
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+ ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> scorerCaptor =
+ ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
+ verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+ scorerCaptor.capture());
+ scorerCaptor.getValue().setScoreChangeCallback(any());
+ mLooper.dispatchAll();
+ verify(mWifiConnectedNetworkScorer).setScoreChangeCallback(any());
+ }
+
+ /**
+ * Verify that Wi-Fi connected scorer receives session ID when start/stop methods are called.
+ */
+ @Test
+ public void verifyScorerReceiveSessionIdWhenStartStopIsCalled() throws Exception {
+ mExecutor = new SynchronousExecutor();
+ mWifiManager.setWifiConnectedNetworkScorer(mExecutor, mWifiConnectedNetworkScorer);
+ ArgumentCaptor<IWifiConnectedNetworkScorer.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(IWifiConnectedNetworkScorer.Stub.class);
+ verify(mWifiService).setWifiConnectedNetworkScorer(any(IBinder.class),
+ callbackCaptor.capture());
+ callbackCaptor.getValue().start(0);
+ callbackCaptor.getValue().stop(10);
+ mLooper.dispatchAll();
+ verify(mWifiConnectedNetworkScorer).start(0);
+ verify(mWifiConnectedNetworkScorer).stop(10);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java
new file mode 100644
index 000000000000..1479acfe8e20
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/wificond/DeviceWiphyCapabilitiesTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.wificond;
+
+import static org.junit.Assert.assertEquals;
+
+import android.net.wifi.ScanResult;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.wificond.DeviceWiphyCapabilities}.
+ */
+@SmallTest
+public class DeviceWiphyCapabilitiesTest {
+ @Before
+ public void setUp() {}
+
+ /**
+ * DeviceWiphyCapabilities object can be serialized and deserialized, while keeping the
+ * values unchanged.
+ */
+ @Test
+ public void canSerializeAndDeserialize() {
+ DeviceWiphyCapabilities capa = new DeviceWiphyCapabilities();
+
+ capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true);
+ capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true);
+ capa.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false);
+
+ Parcel parcel = Parcel.obtain();
+ capa.writeToParcel(parcel, 0);
+ // Rewind the pointer to the head of the parcel.
+ parcel.setDataPosition(0);
+ DeviceWiphyCapabilities capaDeserialized =
+ DeviceWiphyCapabilities.CREATOR.createFromParcel(parcel);
+
+ assertEquals(capa, capaDeserialized);
+ assertEquals(capa.hashCode(), capaDeserialized.hashCode());
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index f3867c1c3fdf..619c95efb173 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.AlarmManager;
import android.app.test.TestAlarmManager;
import android.content.Context;
+import android.net.wifi.ScanResult;
import android.net.wifi.SoftApInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;
@@ -397,7 +398,6 @@ public class WifiCondManagerTest {
verify(mWifiScannerImpl).unsubscribeScanEvents();
}
-
/**
* Verifies that tearDownInterfaces() returns false when wificond is not started.
*/
@@ -1036,6 +1036,25 @@ public class WifiCondManagerTest {
verify(mSendMgmtFrameCallback).onFailure(WifiCondManager.SEND_MGMT_FRAME_ERROR_TIMEOUT);
}
+ /**
+ * Tests getDeviceWiphyCapabililties
+ */
+ @Test
+ public void testGetDeviceWiphyCapabilities() throws Exception {
+ DeviceWiphyCapabilities capaExpected = new DeviceWiphyCapabilities();
+
+ capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11N, true);
+ capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AC, true);
+ capaExpected.setWifiStandardSupport(ScanResult.WIFI_STANDARD_11AX, false);
+
+ when(mWificond.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME))
+ .thenReturn(capaExpected);
+
+ DeviceWiphyCapabilities capaActual =
+ mWificondControl.getDeviceWiphyCapabilities(TEST_INTERFACE_NAME);
+ assertEquals(capaExpected, capaActual);
+ }
+
// Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it
// matches the provided frequency set and ssid set.
private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> {