Merge "Telephony module will staticlly link IState.java"
diff --git a/Android.bp b/Android.bp
index 273650f..a587372 100644
--- a/Android.bp
+++ b/Android.bp
@@ -225,7 +225,6 @@
":framework-mca-filterfw-sources",
":framework-mca-filterpacks-sources",
":framework-media-sources",
- ":framework-mime-sources",
":framework-mms-sources",
":framework-opengl-sources",
":framework-rs-sources",
@@ -267,10 +266,19 @@
}
filegroup {
+ name: "framework-updatable-sources",
+ srcs: [
+ ":framework-sdkext-sources",
+ ":updatable-media-srcs",
+ ]
+}
+
+filegroup {
name: "framework-all-sources",
srcs: [
+ ":framework-mime-sources",
":framework-non-updatable-sources",
- ":updatable-media-srcs",
+ ":framework-updatable-sources",
],
}
@@ -325,6 +333,7 @@
"android.hardware.radio-V1.2-java",
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
+ "android.hardware.radio-V1.5-java",
"android.hardware.thermal-V1.0-java-constants",
"android.hardware.thermal-V1.0-java",
"android.hardware.thermal-V1.1-java",
@@ -375,7 +384,6 @@
static_libs: [
"framework-internal-utils",
- "mimemap",
],
dxflags: [
@@ -437,6 +445,12 @@
"services-platform-compat-config",
"media-provider-platform-compat-config",
"services-devicepolicy-platform-compat-config",
+ "services-core-platform-compat-config",
+ ],
+ static_libs: [
+ // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
+ // in favor of an API stubs dependency in java_library "framework" below.
+ "mimemap",
],
// For backwards compatibility.
stem: "framework",
@@ -572,6 +586,12 @@
],
}
+java_library {
+ name: "framework-annotations-lib",
+ srcs: [ ":framework-annotations" ],
+ sdk_version: "current",
+}
+
filegroup {
name: "framework-networkstack-shared-srcs",
srcs: [
@@ -772,7 +792,7 @@
filegroup {
name: "incremental_aidl",
srcs: [
- "core/java/android/os/incremental/IIncrementalService.aidl",
+ "core/java/android/os/incremental/IIncrementalManager.aidl",
"core/java/android/os/incremental/IIncrementalServiceProxy.aidl",
"core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl",
"core/java/android/os/incremental/IncrementalFileSystemControlParcel.aidl",
@@ -1015,14 +1035,15 @@
stubs_defaults {
name: "framework-doc-stubs-default",
srcs: [
+ ":framework-mime-sources",
":framework-non-updatable-sources",
+ ":framework-updatable-sources",
"core/java/**/*.logtags",
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
":core-current-stubs-source",
":core_public_api_files",
- ":updatable-media-srcs",
"test-mock/src/**/*.java",
"test-runner/src/**/*.java",
],
@@ -1080,12 +1101,12 @@
name: "metalava-api-stubs-default",
srcs: [
":framework-non-updatable-sources",
+ ":framework-updatable-sources",
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
":core-current-stubs-source",
":core_public_api_files",
- ":updatable-media-srcs",
":ike-api-srcs",
],
libs: ["framework-internal-utils"],
@@ -1621,7 +1642,6 @@
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
"core/java/com/android/internal/util/UserIcons.java",
- "core/java/com/android/internal/util/XmlUtils.java",
],
}
diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp
index 2cd7aa0..4ebafce8 100644
--- a/apex/appsearch/service/Android.bp
+++ b/apex/appsearch/service/Android.bp
@@ -21,4 +21,7 @@
"framework",
"services.core",
],
+ static_libs: [
+ "icing-java-proto-lite",
+ ]
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
new file mode 100644
index 0000000..5ac922c
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -0,0 +1,168 @@
+/*
+ * 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.appsearch.impl;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Fake in-memory implementation of the Icing key-value store and reverse index.
+ * <p>
+ * Currently, only queries by single exact term are supported. There is no support for persistence,
+ * namespaces, i18n tokenization, or schema.
+ *
+ * @hide
+ */
+public class FakeIcing {
+ private final AtomicInteger mNextDocId = new AtomicInteger();
+ private final Map<String, Integer> mUriToDocIdMap = new ArrayMap<>();
+ /** Array of Documents where index into the array is the docId. */
+ private final SparseArray<DocumentProto> mDocStore = new SparseArray<>();
+ /** Map of term to posting-list (the set of DocIds containing that term). */
+ private final Map<String, Set<Integer>> mIndex = new ArrayMap<>();
+
+ /**
+ * Inserts a document into the index.
+ *
+ * @param document The document to insert.
+ */
+ public void put(@NonNull DocumentProto document) {
+ String uri = document.getUri();
+
+ // Update mDocIdMap
+ Integer docId = mUriToDocIdMap.get(uri);
+ if (docId != null) {
+ // Delete the old doc
+ mDocStore.remove(docId);
+ }
+
+ // Allocate a new docId
+ docId = mNextDocId.getAndIncrement();
+ mUriToDocIdMap.put(uri, docId);
+
+ // Update mDocStore
+ mDocStore.put(docId, document);
+
+ // Update mIndex
+ indexDocument(docId, document);
+ }
+
+ /**
+ * Retrieves a document from the index.
+ *
+ * @param uri The URI of the document to retrieve.
+ * @return The body of the document, or {@code null} if no such document exists.
+ */
+ @Nullable
+ public DocumentProto get(@NonNull String uri) {
+ Integer docId = mUriToDocIdMap.get(uri);
+ if (docId == null) {
+ return null;
+ }
+ return mDocStore.get(docId);
+ }
+
+ /**
+ * Returns documents containing the given term.
+ *
+ * @param term A single exact term to look up in the index.
+ * @return The matching documents, or an empty {@code List} if no documents match.
+ */
+ @NonNull
+ public List<DocumentProto> query(@NonNull String term) {
+ String normTerm = normalizeString(term);
+ Set<Integer> docIds = mIndex.get(normTerm);
+ if (docIds == null || docIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<DocumentProto> matches = new ArrayList<>(docIds.size());
+ for (int docId : docIds) {
+ DocumentProto document = mDocStore.get(docId);
+ if (document != null) {
+ matches.add(document);
+ }
+ }
+ return matches;
+ }
+
+ /**
+ * Deletes a document by its URI.
+ *
+ * @param uri The URI of the document to be deleted.
+ */
+ public void delete(@NonNull String uri) {
+ // Update mDocIdMap
+ Integer docId = mUriToDocIdMap.get(uri);
+ if (docId != null) {
+ // Delete the old doc
+ mDocStore.remove(docId);
+ mUriToDocIdMap.remove(uri);
+ }
+ }
+
+ private void indexDocument(int docId, DocumentProto document) {
+ for (PropertyProto property : document.getPropertiesList()) {
+ for (String stringValue : property.getStringValuesList()) {
+ String[] words = normalizeString(stringValue).split("\\s+");
+ for (String word : words) {
+ indexTerm(docId, word);
+ }
+ }
+ for (Long longValue : property.getInt64ValuesList()) {
+ indexTerm(docId, longValue.toString());
+ }
+ for (Double doubleValue : property.getDoubleValuesList()) {
+ indexTerm(docId, doubleValue.toString());
+ }
+ for (Boolean booleanValue : property.getBooleanValuesList()) {
+ indexTerm(docId, booleanValue.toString());
+ }
+ // Intentionally skipping bytes values
+ for (DocumentProto documentValue : property.getDocumentValuesList()) {
+ indexDocument(docId, documentValue);
+ }
+ }
+ }
+
+ private void indexTerm(int docId, String term) {
+ Set<Integer> postingList = mIndex.get(term);
+ if (postingList == null) {
+ postingList = new ArraySet<>();
+ mIndex.put(term, postingList);
+ }
+ postingList.add(docId);
+ }
+
+ /** Strips out punctuation and converts to lowercase. */
+ private static String normalizeString(String input) {
+ return input.replaceAll("\\p{P}", "").toLowerCase(Locale.getDefault());
+ }
+}
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
new file mode 100644
index 0000000..b8dcb90
--- /dev/null
+++ b/apex/sdkext/Android.bp
@@ -0,0 +1,32 @@
+// 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.
+
+apex {
+ name: "com.android.sdkext",
+ manifest: "manifest.json",
+ java_libs: [ "framework-sdkext" ],
+ key: "com.android.sdkext.key",
+ certificate: ":com.android.sdkext.certificate",
+}
+
+apex_key {
+ name: "com.android.sdkext.key",
+ public_key: "com.android.sdkext.avbpubkey",
+ private_key: "com.android.sdkext.pem",
+}
+
+android_app_certificate {
+ name: "com.android.sdkext.certificate",
+ certificate: "com.android.sdkext",
+}
diff --git a/apex/sdkext/OWNERS b/apex/sdkext/OWNERS
new file mode 100644
index 0000000..feb2742
--- /dev/null
+++ b/apex/sdkext/OWNERS
@@ -0,0 +1 @@
+hansson@google.com
diff --git a/apex/sdkext/com.android.sdkext.avbpubkey b/apex/sdkext/com.android.sdkext.avbpubkey
new file mode 100644
index 0000000..8f47741
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.avbpubkey
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.pem b/apex/sdkext/com.android.sdkext.pem
new file mode 100644
index 0000000..8164601
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAr72pTSavrziDP54AtQZlRclDxJf9HXRZwFRbYx9hWZ4z7ZtO
+pNBDPvPJCiAOVUsILgCQhBUolz2dyLob25Fd0PVp0n9ibIPEQYjTfHjOK40qb57N
+LhEp2ceGiAfsywPSi0TH1PQ6JgbCe/RM4TefI/sj3gYJPka3ksMvylhMIgUVLgME
+kYizhzwHqlLMspB858SioREZdGwcdZrMMIajGFU69Q9ZRDBzhPvxyKhYoObcOtk1
+uVaiE/fNoi3wKGJz2l2vhUuNrQW7MWlVMag+Qes4YACUTk9LZrOVFEJFjWc8xGUi
+ABtfKGs5JgNr/sWnhvifLn8lJuf0/BJmjD+L5QwXYs2cS7gcZJtTM12J94r0Twgw
+wF2lNmIxAE9sYqj5Rh3dIlTPE5vMUECmQEGjIBB/hzT65VxVqSjU/IlS506JTg3p
+IOQtZ15cUzTBpda4jrvqcq6RNVvgBCu2bV5D8Z4z9FUlPyvD+Zq/6lcoJfLtznAs
+G2463hyPAHTGBIcZ5p5bTuGxoAb6ivyqo4b9Qi4yYA6je9HJmuy8T3Mn5JROoeu9
+BH1K54r/mpT4TQPwuKUvRRtBAV2OPHjo+zp0Gd4Y6rxDYxEIdfEae7pQr/QExSPB
+q/QCr9RhixR1mO373LHuja+MxdAxIxugb2HTS61PQo+PbYrhJMcVuxTwJOECAwEA
+AQKCAgAH7ToRrMkH0ji5SdsmTx+KQkW4PFLCXVke/68PjX7KmAQnl3W4oVwnHr/W
+oROEbVn1GTlre7jU+YaAY0SWZrwgjLE1OWGrG1ZizlUbrCdAd6GOX09J4KROml1L
+DXB0x7tbZMLOrCVjSbLD/ITrM6MN8Gnxvbv0/yOQjxU8vzbP4gLOjHxMRCo001RV
+Ll7lPvcjTQ84zJilU6sE8vJ6zdfVZSK/ou2X0cekG+kP7+fvefo8/UcbEPlGhUrV
+IdVPPQGUu90K2hmN0FBdLi8Vik0klAN68Qu/bHwuKbNzsnmIoztucFFUR+fG3u84
+87aPS0L/J3+mjT2Tv6qhJANUGBmrK/h7MkelpKXlRTCITJLX9xP7hfSbJ4f6aLVq
+ZYPPciGxSBbUDgAwvPtOlMDzccg7YsYyiBBO28wh8MN97rePmc0z6nGmjeXhcbCC
+QktG50VYFCyqp5muKgqQmRfRjHFHLWs8GEqgxMeEL3U3HjYfCYr+6E8Sr5OnOBeH
+3buCi1+zgnNYCvbamgY/OJmW7f9h5O31hxmTplc2E1ZuxUGQZthabt1rN3bmNkyf
+KUmPwnIYkDkWBSV5lzyQExfS3/EVvj0EnHhx8faamimNrGo8xCcfnLT3c0WEFVmo
+yIyVRX3EpXJFM2JkeJ21/IEZXTzHSoNxk12CBG8i8lLSflWSMQKCAQEA2ZqVnOuV
+SZfLCUYUUh8Hvhc5zONstfq7ma1Zsttsdaj9t68nLRiBDvLOGnMjDkYZzoSd4fyl
+oy+YqWGBqcqa5kg1NOCH0I46p9d8RcWAfDnB4sqbLgWh70qsvni6igRijmsMDvkA
+U9HeEdPaLCjQ4UXw7GQvN5rRxuRt+OSqV3tV/Pk9JYyYjz7faC8dmbKDrWHHuOvm
+/9y6Xy+L5IgftykNlUeddSCIoMOAadM7BiRjsrHnOYBQ8xBcn0OYafpIswItrgVi
+IrsPJaBFidx8QYK4MVibyka6U0cm28OocDSPtSk/4jrnCEEhLjFUnWwuMXuBGlrd
+W7wP/muoJqb1VwKCAQEAzsAT90kkOCvAcrfGRE3KkUjwWAsQyP8u2+27JIQPqrpW
+GfWAzJXFt80TSp0Zf/Lrq3/SQ9n4AaL4K9dcMoreedoQN9C9JI7zPtZAWNrJVUcV
+dq2gZjBQ78+oK7uQgvFNWxga5D+Nh+Y+9Tp537fc5HIh0Y13PgsxxPk2OnZJTvLX
+HM5H7Aua9ssmqChsrKihuUsDSPozfBz+H7FNHEdKMqVLqJJSK6m0uMxuLovdVfka
+5S7iBMjEGZc46Iz3ckE0pdOiQLooNqfEQqFe5Uou/KZxxKI1NW25rEEBTVyQWt+2
+BNUCfKP7noZ45u5sUY3eJrgI7BrPEDbNS81WYaLchwKCAQA8Q4mHyd6wYO+EA/qA
+u8NDK9+AFMP4qhXme5HJ7Obetwx9IG7zGEQ1xZy6yoQ84cEn5qZq/bNJvFbFIhHs
+2gWIHRtPJ5e1dI5eCVmLYSUyQjSmAIJ1fm3YfY/VuE3BB3HcC11tkBw9GnQr78YO
+UMd4fAw7C4vgFGpgcMbcFUfvrmKkCsqaaZOeqETq75F9DWlWTSwo1HxHA/RBhENz
+6RcPfLkcTJcY5wevrjUUGcHQ86cAyDBHRngkuLVODkRZpU0Y9lN8TFVfVPre6sIX
+ag6nffJRCD8tB+V2RtBGMKunV4ctHt1oY/Oz34W260aJynoIjjG1ANEpJK4xQdNx
+0O9FAoIBAQCz2AGGKemHswdEwveEkuaSWpA3Bekj7lYkmTchHH9EU7JyAkx3qhDD
+QXB2hxGXawf1tsqAmypQwiJ+gGeCz6mW9UkGRF1DX9XX4yc2I5rew2a4RXAxc/Xz
+pP70i8O5I43Wn7FEusOyY2aAis1Y/eb4EQ+56QTAw5wXa3DwidRbCIJ2XDnT6oRy
+CWUnAYMG7ek/9TB2Wq5OWCn2B5S79IdmZsLZb+5qbMT3u1xcwO1Xy8jJc27IGpv6
+ZsDqCTV1/aJ+XQnWpBg28tiV3Sle6pjUzTRJh5AhWcEZRbKMSOiJI/CBY4k2Qq6t
+xuuEdgFjL7T+mTepqehUglcyiPuLEtAhAoIBAQDDQ5pTFOlunfYzm+CIvvImAgy7
+vrEabJYABATytAbXRMMbrKoEdU2ApEDyEW7PgysDnYLAZ+icObnJlBTYvNdlWFly
+XiviGVfpjFWDT9U/gUwFQu2lfEjoiivoGS92USHUy4UMVHiiznnm20VLLkgd3xna
+HUNSDdHIEgzOTmDsKJwMfA9zGckx23JJimPR5ekv6vr6QllYtECs4lTC1gVQAp2f
+5daxHRbkmO6gw1RgQADLkAnYz3aI1jNuHm5VyAZGt/d3JCtZ3Wwwejll8uJ4J09G
+oEtqyY9RVeHK50bLO4lyAXFiE+J6qqXjsGC20cpxeZYW5llMY/dhA6WV4YXV
+-----END RSA PRIVATE KEY-----
diff --git a/apex/sdkext/com.android.sdkext.pk8 b/apex/sdkext/com.android.sdkext.pk8
new file mode 100644
index 0000000..ccc0bf4
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.pk8
Binary files differ
diff --git a/apex/sdkext/com.android.sdkext.x509.pem b/apex/sdkext/com.android.sdkext.x509.pem
new file mode 100644
index 0000000..45d2ade
--- /dev/null
+++ b/apex/sdkext/com.android.sdkext.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGIzCCBAugAwIBAgIUXuDL7QvzQh7S6rihWz2KRvCFVT0wDQYJKoZIhvcNAQEL
+BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMRswGQYDVQQDDBJjb20uYW5kcm9pZC5zZGtleHQxIjAgBgkqhkiG9w0BCQEW
+E2FuZHJvaWRAYW5kcm9pZC5jb20wIBcNMTkxMjAyMTQyNDM0WhgPNDc1NzEwMjgx
+NDI0MzRaMIGfMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG
+A1UEBwwNTW91bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwH
+QW5kcm9pZDEbMBkGA1UEAwwSY29tLmFuZHJvaWQuc2RrZXh0MSIwIAYJKoZIhvcN
+AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
+MIICCgKCAgEAxFvZZ6ES1oqAu1K74/ZxnC3SOhHnLISLBgJEe7DqtdpuNFAwvdVO
+RL/HULhDbjYlOhpU2x3SavDIZZ2lRfiS9Q+M25WftxTRHVjBcpgwbV77TVxPKlAa
+tVN2lUVOY+s4QAVMNIXjC4kCKK/pCQtacH715EtdV47fWdg/Nx4iP/Aord8k3KGI
+9iI2ZOUjaugTRxu5lKRNDrv0bw5rEzyYmDyMud+kR/iS3/5oog57wPE0ffAkZXWE
+p3L2Cejre3ekCizsvVh6EmH6ForKLtL6f0z5Zir1f4R9+YcENspTlJR3pDhg7y3I
+uTQT/iDCtV0l+g2PjGZPEeAQHND3+kDQR7Sno/WC1Nhws6vcu1MdrC+kIh1ewx4y
+8moy/yqb5M98PJDzTSi/AOTB/OiqLXo/T8rjLBmirs9y3fTT6gJ6qXxOWgt8dle9
+7TBfa84Xi8uVY61c+A+YI0nLal7QDPsP3RPv5sJSQ9x9YnweVfD9Q0EOi52sSNu+
+QuN/kcUrMgPgha20VhfH/CkkPDyIp6aZyHHM69MIl+cYEm8vPa5uy3dosuRomT0f
+I4HOBjULDIuj+rIi+Rg3qHvmpuejwZXI/FBNWIhLEUG3ytgksjMaBoYAYflGdrcj
+BQexuF3EO+j4uo7JGjNcaT7wRoCH9gt29VHckDg2qz6VWXrlpmME4UkCAwEAAaNT
+MFEwHQYDVR0OBBYEFISN2nmUHllgPZMZ62U7mU3ZxzlXMB8GA1UdIwQYMBaAFISN
+2nmUHllgPZMZ62U7mU3ZxzlXMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
+BQADggIBAFHIwyBNIVyHXUsDUdcjxfojXQsF/BCL9ehE3pgdkvDfQanaIREWn0nc
+oCFDFkYMRqaXOGC5TKq4OCjXOLsdfODt8HQ3F9J1B0ghQ5tfOdw7xDugNAszqP/Q
+h7kpvqLTycjrqOeZ5KjxEEYtP/KlUmALgOKcTcSH+XhWyxhjF4j24T9F2yJRr3/A
+r1NGU/djH953bHKC8OpJ2teUpDLA4TxVp/EhslH2eVigF80c/w74QPLEWkD9zv/4
+YeRg/R5N83zHs99NtlWMIeHfK6fUbzMyaSZtvm+jK20tkByQb/OQRed+drk25MtL
+68IRvxqri367qRScdpTZbu0ByLO4X8gFdubRUWr+tcO4pZX+DJRVriExbOkU2xhS
+Vtslq23V/hwTuUNm1CXjR70mPS13BTmHrIQDqLoIw/WTQlGh+vxnlAFRIHM3KB2c
+OdzUBu+NcB4aZEd0KKtct600A0DKPr1MQPb5jDq9wEtPSQYwMF0nRFNnXltbrXMd
+4hhwnhKr74fVMUmb+7eQP56XE/Nk4D+npMO54vv1pav+DI2/nxCND9BOLBweY38p
+Tvd2RjesMok0zXuVXiCIu4GEpwo7WkSnv25xrb0Ey2M8QWnGNnCcX7Kv6ip3RdWy
+HiN0G8RJrs/yNEVSDRx8ZhtwTpXVPQxbARbmhNF4/fnolElkmrMP
+-----END CERTIFICATE-----
diff --git a/apex/sdkext/framework/Android.bp b/apex/sdkext/framework/Android.bp
new file mode 100644
index 0000000..b17f0f8
--- /dev/null
+++ b/apex/sdkext/framework/Android.bp
@@ -0,0 +1,30 @@
+// 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.
+
+filegroup {
+ name: "framework-sdkext-sources",
+ srcs: [
+ "java/**/*.java",
+ ],
+ path: "java",
+}
+
+java_library {
+ name: "framework-sdkext",
+ srcs: [ ":framework-sdkext-sources" ],
+ sdk_version: "system_current",
+ libs: [ "framework-annotations-lib" ],
+ permitted_packages: [ "android.os.ext" ],
+ installable: true,
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
new file mode 100644
index 0000000..c039a82
--- /dev/null
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -0,0 +1,53 @@
+/*
+ * 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.os.ext;
+
+import android.annotation.IntDef;
+import android.os.Build.VERSION_CODES;
+import android.os.SystemProperties;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** @hide */
+public class SdkExtensions {
+
+ private static final int R_EXTENSION_INT;
+ static {
+ R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
+ }
+
+ /** Values suitable as parameters for {@link #getExtensionVersion(int)}. */
+ @IntDef(value = { VERSION_CODES.R })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SdkVersion {}
+
+ /**
+ * Return the version of the extension to the given SDK.
+ *
+ * @param sdk the SDK version to get the extension version of.
+ * @see SdkVersion
+ * @throws IllegalArgumentException if sdk is not an sdk version with extensions
+ */
+ public static int getExtensionVersion(@SdkVersion int sdk) {
+ if (sdk < VERSION_CODES.R) {
+ throw new IllegalArgumentException();
+ }
+ return R_EXTENSION_INT;
+ }
+
+}
diff --git a/apex/sdkext/framework/java/android/os/ext/package.html b/apex/sdkext/framework/java/android/os/ext/package.html
new file mode 100644
index 0000000..34c1697
--- /dev/null
+++ b/apex/sdkext/framework/java/android/os/ext/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+Provides APIs to interface with the SDK extensions.
+</BODY>
+</HTML>
diff --git a/apex/sdkext/framework/tests/Android.bp b/apex/sdkext/framework/tests/Android.bp
new file mode 100644
index 0000000..3d5dbb3
--- /dev/null
+++ b/apex/sdkext/framework/tests/Android.bp
@@ -0,0 +1,10 @@
+android_test {
+ name: "framework-sdkext-tests",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+ static_libs: [ "framework-sdkext" ],
+ platform_apis: true,
+}
diff --git a/apex/sdkext/framework/tests/AndroidManifest.xml b/apex/sdkext/framework/tests/AndroidManifest.xml
new file mode 100644
index 0000000..831f132
--- /dev/null
+++ b/apex/sdkext/framework/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.sdkext.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.sdkext.tests" />
+
+</manifest>
diff --git a/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
new file mode 100644
index 0000000..6885110
--- /dev/null
+++ b/apex/sdkext/framework/tests/src/android/os/ext/SdkExtensionsTest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.os.ext;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class SdkExtensionsTest extends TestCase {
+
+ @SmallTest
+ public void testBadArgument() throws Exception {
+ try {
+ SdkExtensions.getExtensionVersion(Build.VERSION_CODES.Q);
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) { }
+
+ try {
+ SdkExtensions.getExtensionVersion(999999);
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) { }
+ }
+
+ @SmallTest
+ public void testDefault() throws Exception {
+ int r = SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R);
+ assertTrue(r >= 0);
+ }
+
+}
diff --git a/apex/sdkext/manifest.json b/apex/sdkext/manifest.json
new file mode 100644
index 0000000..048f5c4
--- /dev/null
+++ b/apex/sdkext/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.sdkext",
+ "version": 1
+}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 62c77ef..f11d4f8 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -1163,12 +1163,12 @@
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
wallClockNanos);
- e.writeLong(wifiInfo.getTimeStamp());
+ e.writeLong(wifiInfo.getTimeSinceBootMillis());
e.writeInt(wifiInfo.getStackState());
- e.writeLong(wifiInfo.getControllerTxTimeMillis());
- e.writeLong(wifiInfo.getControllerRxTimeMillis());
- e.writeLong(wifiInfo.getControllerIdleTimeMillis());
- e.writeLong(wifiInfo.getControllerEnergyUsed());
+ e.writeLong(wifiInfo.getControllerTxDurationMillis());
+ e.writeLong(wifiInfo.getControllerRxDurationMillis());
+ e.writeLong(wifiInfo.getControllerIdleDurationMillis());
+ e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
pulledData.add(e);
} catch (RemoteException e) {
Slog.e(TAG,
@@ -2183,6 +2183,20 @@
e.writeLong(op.getBackgroundRejectCount(OP_FLAGS_ALL_TRUSTED));
e.writeLong(op.getForegroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
e.writeLong(op.getBackgroundAccessDuration(OP_FLAGS_ALL_TRUSTED));
+
+ String perm = AppOpsManager.opToPermission(op.getOpCode());
+ if (perm == null) {
+ e.writeBoolean(false);
+ } else {
+ PermissionInfo permInfo;
+ try {
+ permInfo = mContext.getPackageManager().getPermissionInfo(perm, 0);
+ e.writeBoolean(permInfo.getProtection() == PROTECTION_DANGEROUS);
+ } catch (PackageManager.NameNotFoundException exception) {
+ e.writeBoolean(false);
+ }
+ }
+
pulledData.add(e);
}
}
diff --git a/api/current.txt b/api/current.txt
index c3b8d26..04fdaec 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9899,6 +9899,7 @@
method public abstract void sendOrderedBroadcast(@RequiresPermission android.content.Intent, @Nullable String);
method public abstract void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+ method public void sendOrderedBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable String, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyBroadcast(@RequiresPermission android.content.Intent);
method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
@@ -23063,6 +23064,7 @@
public final class GnssStatus {
method @FloatRange(from=0, to=360) public float getAzimuthDegrees(@IntRange(from=0) int);
+ method @FloatRange(from=0, to=63) public float getBasebandCn0DbHz(@IntRange(from=0) int);
method @FloatRange(from=0) public float getCarrierFrequencyHz(@IntRange(from=0) int);
method @FloatRange(from=0, to=63) public float getCn0DbHz(@IntRange(from=0) int);
method public int getConstellationType(@IntRange(from=0) int);
@@ -23070,6 +23072,7 @@
method @IntRange(from=0) public int getSatelliteCount();
method @IntRange(from=1, to=200) public int getSvid(@IntRange(from=0) int);
method public boolean hasAlmanacData(@IntRange(from=0) int);
+ method public boolean hasBasebandCn0DbHz(@IntRange(from=0) int);
method public boolean hasCarrierFrequencyHz(@IntRange(from=0) int);
method public boolean hasEphemerisData(@IntRange(from=0) int);
method public boolean usedInFix(@IntRange(from=0) int);
@@ -23085,7 +23088,7 @@
public static final class GnssStatus.Builder {
ctor public GnssStatus.Builder();
- method @NonNull public android.location.GnssStatus.Builder addSatellite(int, @IntRange(from=1, to=200) int, @FloatRange(from=0, to=63) float, @FloatRange(from=0xffffffa6, to=90) float, @FloatRange(from=0, to=360) float, boolean, boolean, boolean, boolean, @FloatRange(from=0) float);
+ method @NonNull public android.location.GnssStatus.Builder addSatellite(int, @IntRange(from=1, to=200) int, @FloatRange(from=0, to=63) float, @FloatRange(from=0xffffffa6, to=90) float, @FloatRange(from=0, to=360) float, boolean, boolean, boolean, boolean, @FloatRange(from=0) float, boolean, @FloatRange(from=0, to=63) float);
method @NonNull public android.location.GnssStatus build();
method @NonNull public android.location.GnssStatus.Builder clearSatellites();
}
@@ -28925,6 +28928,7 @@
method @Nullable public String getInterfaceName();
method @NonNull public java.util.List<android.net.LinkAddress> getLinkAddresses();
method public int getMtu();
+ method @Nullable public android.net.IpPrefix getNat64Prefix();
method @Nullable public String getPrivateDnsServerName();
method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
method public boolean isPrivateDnsActive();
@@ -28935,6 +28939,7 @@
method public void setInterfaceName(@Nullable String);
method public void setLinkAddresses(@NonNull java.util.Collection<android.net.LinkAddress>);
method public void setMtu(int);
+ method public void setNat64Prefix(@Nullable android.net.IpPrefix);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.LinkProperties> CREATOR;
}
@@ -30244,6 +30249,7 @@
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsEnhancedOpen(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsHiddenSsid(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsMetered(boolean);
+ method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserAllowedToManuallyConnect(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
@@ -30565,6 +30571,7 @@
method @Nullable public android.net.wifi.p2p.WifiP2pWfdInfo getWfdInfo();
method public boolean isGroupOwner();
method public boolean isServiceDiscoveryCapable();
+ method public void update(@NonNull android.net.wifi.p2p.WifiP2pDevice);
method public boolean wpsDisplaySupported();
method public boolean wpsKeypadSupported();
method public boolean wpsPbcSupported();
@@ -44283,6 +44290,7 @@
field public static final int EUTRAN = 3; // 0x3
field public static final int GERAN = 1; // 0x1
field public static final int IWLAN = 5; // 0x5
+ field public static final int NGRAN = 6; // 0x6
field public static final int UNKNOWN = 0; // 0x0
field public static final int UTRAN = 2; // 0x2
}
@@ -44355,6 +44363,45 @@
field public static final int BAND_T810 = 7; // 0x7
}
+ public static final class AccessNetworkConstants.NgranBands {
+ field public static final int BAND_1 = 1; // 0x1
+ field public static final int BAND_12 = 12; // 0xc
+ field public static final int BAND_2 = 2; // 0x2
+ field public static final int BAND_20 = 20; // 0x14
+ field public static final int BAND_25 = 25; // 0x19
+ field public static final int BAND_257 = 257; // 0x101
+ field public static final int BAND_258 = 258; // 0x102
+ field public static final int BAND_260 = 260; // 0x104
+ field public static final int BAND_261 = 261; // 0x105
+ field public static final int BAND_28 = 28; // 0x1c
+ field public static final int BAND_3 = 3; // 0x3
+ field public static final int BAND_34 = 34; // 0x22
+ field public static final int BAND_38 = 38; // 0x26
+ field public static final int BAND_39 = 39; // 0x27
+ field public static final int BAND_40 = 40; // 0x28
+ field public static final int BAND_41 = 41; // 0x29
+ field public static final int BAND_5 = 5; // 0x5
+ field public static final int BAND_50 = 50; // 0x32
+ field public static final int BAND_51 = 51; // 0x33
+ field public static final int BAND_66 = 66; // 0x42
+ field public static final int BAND_7 = 7; // 0x7
+ field public static final int BAND_70 = 70; // 0x46
+ field public static final int BAND_71 = 71; // 0x47
+ field public static final int BAND_74 = 74; // 0x4a
+ field public static final int BAND_75 = 75; // 0x4b
+ field public static final int BAND_76 = 76; // 0x4c
+ field public static final int BAND_77 = 77; // 0x4d
+ field public static final int BAND_78 = 78; // 0x4e
+ field public static final int BAND_79 = 79; // 0x4f
+ field public static final int BAND_8 = 8; // 0x8
+ field public static final int BAND_80 = 80; // 0x50
+ field public static final int BAND_81 = 81; // 0x51
+ field public static final int BAND_82 = 82; // 0x52
+ field public static final int BAND_83 = 83; // 0x53
+ field public static final int BAND_84 = 84; // 0x54
+ field public static final int BAND_86 = 86; // 0x56
+ }
+
public static final class AccessNetworkConstants.UtranBand {
field public static final int BAND_1 = 1; // 0x1
field public static final int BAND_10 = 10; // 0xa
@@ -55154,12 +55201,12 @@
}
public class WebView extends android.widget.AbsoluteLayout implements android.view.ViewGroup.OnHierarchyChangeListener android.view.ViewTreeObserver.OnGlobalFocusChangeListener {
- ctor public WebView(android.content.Context);
- ctor public WebView(android.content.Context, android.util.AttributeSet);
- ctor public WebView(android.content.Context, android.util.AttributeSet, int);
- ctor public WebView(android.content.Context, android.util.AttributeSet, int, int);
- ctor @Deprecated public WebView(android.content.Context, android.util.AttributeSet, int, boolean);
- method public void addJavascriptInterface(Object, String);
+ ctor public WebView(@NonNull android.content.Context);
+ ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+ ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+ ctor public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int, int);
+ ctor @Deprecated public WebView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int, boolean);
+ method public void addJavascriptInterface(@NonNull Object, @NonNull String);
method public boolean canGoBack();
method public boolean canGoBackOrForward(int);
method public boolean canGoForward();
@@ -55173,40 +55220,40 @@
method public void clearMatches();
method public void clearSslPreferences();
method @Deprecated public void clearView();
- method public android.webkit.WebBackForwardList copyBackForwardList();
+ method @NonNull public android.webkit.WebBackForwardList copyBackForwardList();
method @Deprecated public android.print.PrintDocumentAdapter createPrintDocumentAdapter();
- method public android.print.PrintDocumentAdapter createPrintDocumentAdapter(String);
- method public android.webkit.WebMessagePort[] createWebMessageChannel();
+ method @NonNull public android.print.PrintDocumentAdapter createPrintDocumentAdapter(@NonNull String);
+ method @NonNull public android.webkit.WebMessagePort[] createWebMessageChannel();
method public void destroy();
method public static void disableWebView();
- method public void documentHasImages(android.os.Message);
+ method public void documentHasImages(@NonNull android.os.Message);
method public static void enableSlowWholeDocumentDraw();
- method public void evaluateJavascript(String, @Nullable android.webkit.ValueCallback<java.lang.String>);
+ method public void evaluateJavascript(@NonNull String, @Nullable android.webkit.ValueCallback<java.lang.String>);
method @Deprecated @Nullable public static String findAddress(String);
method @Deprecated public int findAll(String);
- method public void findAllAsync(String);
+ method public void findAllAsync(@NonNull String);
method public void findNext(boolean);
method public void flingScroll(int, int);
method @Deprecated public void freeMemory();
method @Nullable public android.net.http.SslCertificate getCertificate();
method @android.view.ViewDebug.ExportedProperty(category="webview") public int getContentHeight();
method @Nullable public static android.content.pm.PackageInfo getCurrentWebViewPackage();
- method public android.graphics.Bitmap getFavicon();
- method public android.webkit.WebView.HitTestResult getHitTestResult();
+ method @Nullable public android.graphics.Bitmap getFavicon();
+ method @NonNull public android.webkit.WebView.HitTestResult getHitTestResult();
method @Deprecated @Nullable public String[] getHttpAuthUsernamePassword(String, String);
- method @android.view.ViewDebug.ExportedProperty(category="webview") public String getOriginalUrl();
+ method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getOriginalUrl();
method public int getProgress();
method public boolean getRendererPriorityWaivedWhenNotVisible();
method public int getRendererRequestedPriority();
method @NonNull public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
method @Deprecated @android.view.ViewDebug.ExportedProperty(category="webview") public float getScale();
- method public android.webkit.WebSettings getSettings();
+ method @NonNull public android.webkit.WebSettings getSettings();
method @NonNull public android.view.textclassifier.TextClassifier getTextClassifier();
- method @android.view.ViewDebug.ExportedProperty(category="webview") public String getTitle();
- method @android.view.ViewDebug.ExportedProperty(category="webview") public String getUrl();
+ method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getTitle();
+ method @android.view.ViewDebug.ExportedProperty(category="webview") @Nullable public String getUrl();
method @Nullable public android.webkit.WebChromeClient getWebChromeClient();
method @NonNull public static ClassLoader getWebViewClassLoader();
- method public android.webkit.WebViewClient getWebViewClient();
+ method @NonNull public android.webkit.WebViewClient getWebViewClient();
method @NonNull public android.os.Looper getWebViewLooper();
method @Nullable public android.webkit.WebViewRenderProcess getWebViewRenderProcess();
method @Nullable public android.webkit.WebViewRenderProcessClient getWebViewRenderProcessClient();
@@ -55215,10 +55262,10 @@
method public void goForward();
method public void invokeZoomPicker();
method public boolean isPrivateBrowsingEnabled();
- method public void loadData(String, @Nullable String, @Nullable String);
- method public void loadDataWithBaseURL(@Nullable String, String, @Nullable String, @Nullable String, @Nullable String);
- method public void loadUrl(String, java.util.Map<java.lang.String,java.lang.String>);
- method public void loadUrl(String);
+ method public void loadData(@NonNull String, @Nullable String, @Nullable String);
+ method public void loadDataWithBaseURL(@Nullable String, @NonNull String, @Nullable String, @Nullable String, @Nullable String);
+ method public void loadUrl(@NonNull String, @NonNull java.util.Map<java.lang.String,java.lang.String>);
+ method public void loadUrl(@NonNull String);
method @Deprecated public void onChildViewAdded(android.view.View, android.view.View);
method @Deprecated public void onChildViewRemoved(android.view.View, android.view.View);
method @Deprecated public void onGlobalFocusChanged(android.view.View, android.view.View);
@@ -55229,23 +55276,23 @@
method public boolean pageDown(boolean);
method public boolean pageUp(boolean);
method public void pauseTimers();
- method public void postUrl(String, byte[]);
- method public void postVisualStateCallback(long, android.webkit.WebView.VisualStateCallback);
- method public void postWebMessage(android.webkit.WebMessage, android.net.Uri);
+ method public void postUrl(@NonNull String, @NonNull byte[]);
+ method public void postVisualStateCallback(long, @NonNull android.webkit.WebView.VisualStateCallback);
+ method public void postWebMessage(@NonNull android.webkit.WebMessage, @NonNull android.net.Uri);
method public void reload();
method public void removeJavascriptInterface(@NonNull String);
method public void requestFocusNodeHref(@Nullable android.os.Message);
- method public void requestImageRef(android.os.Message);
- method @Nullable public android.webkit.WebBackForwardList restoreState(android.os.Bundle);
+ method public void requestImageRef(@NonNull android.os.Message);
+ method @Nullable public android.webkit.WebBackForwardList restoreState(@NonNull android.os.Bundle);
method public void resumeTimers();
method @Deprecated public void savePassword(String, String, String);
- method @Nullable public android.webkit.WebBackForwardList saveState(android.os.Bundle);
- method public void saveWebArchive(String);
- method public void saveWebArchive(String, boolean, @Nullable android.webkit.ValueCallback<java.lang.String>);
+ method @Nullable public android.webkit.WebBackForwardList saveState(@NonNull android.os.Bundle);
+ method public void saveWebArchive(@NonNull String);
+ method public void saveWebArchive(@NonNull String, boolean, @Nullable android.webkit.ValueCallback<java.lang.String>);
method @Deprecated public void setCertificate(android.net.http.SslCertificate);
- method public static void setDataDirectorySuffix(String);
- method public void setDownloadListener(android.webkit.DownloadListener);
- method public void setFindListener(android.webkit.WebView.FindListener);
+ method public static void setDataDirectorySuffix(@NonNull String);
+ method public void setDownloadListener(@Nullable android.webkit.DownloadListener);
+ method public void setFindListener(@Nullable android.webkit.WebView.FindListener);
method @Deprecated public void setHorizontalScrollbarOverlay(boolean);
method @Deprecated public void setHttpAuthUsernamePassword(String, String, String, String);
method public void setInitialScale(int);
@@ -55256,9 +55303,9 @@
method public static void setSafeBrowsingWhitelist(@NonNull java.util.List<java.lang.String>, @Nullable android.webkit.ValueCallback<java.lang.Boolean>);
method public void setTextClassifier(@Nullable android.view.textclassifier.TextClassifier);
method @Deprecated public void setVerticalScrollbarOverlay(boolean);
- method public void setWebChromeClient(android.webkit.WebChromeClient);
+ method public void setWebChromeClient(@Nullable android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
- method public void setWebViewClient(android.webkit.WebViewClient);
+ method public void setWebViewClient(@NonNull android.webkit.WebViewClient);
method public void setWebViewRenderProcessClient(@NonNull java.util.concurrent.Executor, @NonNull android.webkit.WebViewRenderProcessClient);
method public void setWebViewRenderProcessClient(@Nullable android.webkit.WebViewRenderProcessClient);
method @Deprecated public boolean shouldDelayChildPressedState();
@@ -55306,8 +55353,8 @@
public class WebView.WebViewTransport {
ctor public WebView.WebViewTransport();
- method public android.webkit.WebView getWebView();
- method public void setWebView(android.webkit.WebView);
+ method @Nullable public android.webkit.WebView getWebView();
+ method public void setWebView(@Nullable android.webkit.WebView);
}
public class WebViewClient {
diff --git a/api/system-current.txt b/api/system-current.txt
index aeb89b2..189585e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -213,6 +213,8 @@
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String WHITELIST_RESTRICTED_PERMISSIONS = "android.permission.WHITELIST_RESTRICTED_PERMISSIONS";
+ field public static final String WIFI_SET_DEVICE_MOBILITY_STATE = "android.permission.WIFI_SET_DEVICE_MOBILITY_STATE";
+ field public static final String WIFI_UPDATE_USABILITY_STATS_SCORE = "android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field public static final String WRITE_DREAM_STATE = "android.permission.WRITE_DREAM_STATE";
field public static final String WRITE_EMBEDDED_SUBSCRIPTIONS = "android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS";
@@ -828,6 +830,7 @@
method @RequiresPermission(android.Manifest.permission.BACKUP) public void backupNow();
method @RequiresPermission(android.Manifest.permission.BACKUP) public android.app.backup.RestoreSession beginRestoreSession();
method @RequiresPermission(android.Manifest.permission.BACKUP) public void cancelBackups();
+ method @RequiresPermission(android.Manifest.permission.BACKUP) public void excludeKeysFromRestore(@NonNull String, @NonNull java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.BACKUP) public long getAvailableRestoreToken(String);
method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getConfigurationIntent(String);
method @RequiresPermission(android.Manifest.permission.BACKUP) public String getCurrentTransport();
@@ -4378,6 +4381,7 @@
public final class IpConfiguration implements android.os.Parcelable {
ctor public IpConfiguration();
ctor public IpConfiguration(@NonNull android.net.IpConfiguration);
+ method public int describeContents();
method @Nullable public android.net.ProxyInfo getHttpProxy();
method @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
method @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
@@ -4386,6 +4390,7 @@
method public void setIpAssignment(@NonNull android.net.IpConfiguration.IpAssignment);
method public void setProxySettings(@NonNull android.net.IpConfiguration.ProxySettings);
method public void setStaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.IpConfiguration> CREATOR;
}
@@ -4438,7 +4443,6 @@
ctor public LinkProperties(@Nullable android.net.LinkProperties);
method public boolean addDnsServer(@NonNull java.net.InetAddress);
method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
- method @Nullable public android.net.IpPrefix getNat64Prefix();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @Nullable public String getTcpBufferSizes();
method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -4452,7 +4456,6 @@
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
- method public void setNat64Prefix(@Nullable android.net.IpPrefix);
method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(@Nullable String);
@@ -5273,18 +5276,30 @@
public final class SoftApConfiguration implements android.os.Parcelable {
method public int describeContents();
+ method public int getBand();
method @Nullable public android.net.MacAddress getBssid();
+ method public int getChannel();
+ method public int getSecurityType();
method @Nullable public String getSsid();
method @Nullable public String getWpa2Passphrase();
+ method public boolean isHiddenSsid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int BAND_2GHZ = 0; // 0x0
+ field public static final int BAND_5GHZ = 1; // 0x1
+ field public static final int BAND_ANY = -1; // 0xffffffff
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
+ field public static final int SECURITY_TYPE_OPEN = 0; // 0x0
+ field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1
}
public static final class SoftApConfiguration.Builder {
ctor public SoftApConfiguration.Builder();
ctor public SoftApConfiguration.Builder(@NonNull android.net.wifi.SoftApConfiguration);
method @NonNull public android.net.wifi.SoftApConfiguration build();
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBand(int);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setBssid(@Nullable android.net.MacAddress);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int);
+ method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
}
@@ -5304,6 +5319,32 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR;
}
+ public final class WifiActivityEnergyInfo implements android.os.Parcelable {
+ ctor public WifiActivityEnergyInfo(long, int, long, long, long, long, long);
+ method public int describeContents();
+ method public long getControllerEnergyUsedMicroJoules();
+ method public long getControllerIdleDurationMillis();
+ method public long getControllerRxDurationMillis();
+ method public long getControllerScanDurationMillis();
+ method public long getControllerTxDurationMillis();
+ method public int getStackState();
+ method public long getTimeSinceBootMillis();
+ method public boolean isValid();
+ method public void setControllerEnergyUsedMicroJoules(long);
+ method public void setControllerIdleDurationMillis(long);
+ method public void setControllerRxDurationMillis(long);
+ method public void setControllerScanDurationMillis(long);
+ method public void setControllerTxDurationMillis(long);
+ method public void setStackState(int);
+ method public void setTimeSinceBootMillis(long);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiActivityEnergyInfo> CREATOR;
+ field public static final int STACK_STATE_INVALID = 0; // 0x0
+ field public static final int STACK_STATE_STATE_ACTIVE = 1; // 0x1
+ field public static final int STACK_STATE_STATE_IDLE = 3; // 0x3
+ field public static final int STACK_STATE_STATE_SCANNING = 2; // 0x2
+ }
+
public final class WifiClient implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.net.MacAddress getMacAddress();
@@ -5422,7 +5463,7 @@
}
public class WifiManager {
- method @RequiresPermission("android.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.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(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);
@@ -5438,8 +5479,9 @@
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
method public int getVerboseLoggingLevel();
- method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
method public boolean isApMacRandomizationSupported();
method public boolean isConnectedMacRandomizationSupported();
@@ -5455,26 +5497,29 @@
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerTrafficStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.TrafficStateCallback);
- method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreBackupData(@NonNull byte[]);
+ method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSoftApBackupData(@NonNull byte[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void restoreSupplicantBackupData(@NonNull byte[], @NonNull byte[]);
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
+ method @Nullable @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
- method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
+ method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
+ 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(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);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
- method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startTetheredHotspot(@Nullable android.net.wifi.SoftApConfiguration);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterNetworkRequestMatchCallback(@NonNull android.net.wifi.WifiManager.NetworkRequestMatchCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterSoftApCallback(@NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void unregisterTrafficStateCallback(@NonNull android.net.wifi.WifiManager.TrafficStateCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
- method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
+ method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public void updateWifiUsabilityScore(int, int, int);
field public static final String ACTION_LINK_CONFIGURATION_CHANGED = "android.net.wifi.LINK_CONFIGURATION_CHANGED";
field public static final String ACTION_PASSPOINT_LAUNCH_OSU_VIEW = "android.net.wifi.action.PASSPOINT_LAUNCH_OSU_VIEW";
field public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
@@ -5577,6 +5622,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<java.lang.Integer> getAvailableChannels(int);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean getScanResults();
method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public java.util.List<android.net.wifi.ScanResult> getSingleScanResults();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void registerScanListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiScanner.ScanListener);
method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void setScanningEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener, android.os.WorkSource);
@@ -5588,6 +5634,7 @@
method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void stopScan(android.net.wifi.WifiScanner.ScanListener);
method @Deprecated public void stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener);
method @Deprecated public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
+ method public void unregisterScanListener(@NonNull android.net.wifi.WifiScanner.ScanListener);
field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
field public static final int MIN_SCAN_PERIOD_MS = 1000; // 0x3e8
field public static final int REASON_DUPLICATE_REQEUST = -5; // 0xfffffffb
@@ -5600,6 +5647,9 @@
field public static final int REPORT_EVENT_AFTER_EACH_SCAN = 1; // 0x1
field public static final int REPORT_EVENT_FULL_SCAN_RESULT = 2; // 0x2
field public static final int REPORT_EVENT_NO_BATCH = 4; // 0x4
+ field public static final int SCAN_TYPE_HIGH_ACCURACY = 2; // 0x2
+ field public static final int SCAN_TYPE_LOW_LATENCY = 0; // 0x0
+ field public static final int SCAN_TYPE_LOW_POWER = 1; // 0x1
field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
@@ -5668,6 +5718,7 @@
ctor public WifiScanner.ScanSettings();
field public int band;
field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+ field @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public final java.util.List<android.net.wifi.WifiScanner.ScanSettings.HiddenNetwork> hiddenNetworks;
field public boolean hideFromAppOps;
field public boolean ignoreLocationSettings;
field public int maxPeriodInMs;
@@ -5676,6 +5727,12 @@
field public int periodInMs;
field public int reportEvents;
field public int stepCount;
+ field @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public int type;
+ }
+
+ public static class WifiScanner.ScanSettings.HiddenNetwork {
+ ctor public WifiScanner.ScanSettings.HiddenNetwork(@NonNull String);
+ field @NonNull public final String ssid;
}
@Deprecated public static interface WifiScanner.WifiChangeListener extends android.net.wifi.WifiScanner.ActionListener {
@@ -8007,6 +8064,10 @@
field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
}
+ public final class ConnectionRequest implements android.os.Parcelable {
+ method @Nullable public String getTelecomCallId();
+ }
+
public abstract class ConnectionService extends android.app.Service {
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
@@ -8214,6 +8275,7 @@
method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
method public int getAllPhoneAccountsCount();
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
method public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index ab0f0f9..fcf5178 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -65,7 +65,7 @@
MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #1:
MissingNullability: android.media.tv.TvRecordingClient.RecordingCallback#onEvent(String, String, android.os.Bundle) parameter #2:
-
+
MissingNullability: android.net.wifi.rtt.RangingRequest.Builder#addResponder(android.net.wifi.rtt.ResponderConfig):
MissingNullability: android.printservice.recommendation.RecommendationService#attachBaseContext(android.content.Context) parameter #0:
@@ -181,6 +181,8 @@
Bare field saePasswordId must be marked final, or moved behind accessors if mutable
MutableBareField: android.net.wifi.WifiConfiguration#shared:
Bare field shared must be marked final, or moved behind accessors if mutable
+MutableBareField: android.net.wifi.WifiScanner.ScanSettings#type:
+ Bare field type must be marked final, or moved behind accessors if mutable
NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
diff --git a/api/test-current.txt b/api/test-current.txt
index 9445322..9ac789e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1466,7 +1466,6 @@
ctor public LinkProperties(@Nullable android.net.LinkProperties);
method public boolean addDnsServer(@NonNull java.net.InetAddress);
method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
- method @Nullable public android.net.IpPrefix getNat64Prefix();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @Nullable public String getTcpBufferSizes();
method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -1480,7 +1479,6 @@
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
- method public void setNat64Prefix(@Nullable android.net.IpPrefix);
method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(@Nullable String);
@@ -2974,6 +2972,23 @@
field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800
}
+ public final class ConnectionRequest implements android.os.Parcelable {
+ method @Nullable public String getTelecomCallId();
+ }
+
+ public static final class ConnectionRequest.Builder {
+ ctor public ConnectionRequest.Builder();
+ method @NonNull public android.telecom.ConnectionRequest build();
+ method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setTelecomCallId(@NonNull String);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int);
+ }
+
public static class PhoneAccount.Builder {
method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
}
@@ -2987,6 +3002,7 @@
}
public class TelecomManager {
+ method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode();
method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
@@ -4274,6 +4290,7 @@
field public static final String PERSIST_PREFIX = "persist.sys.fflag.override.";
field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
+ field public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 49cc3c4..b9051da 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -6362,7 +6362,8 @@
optional bool is_preinstalled = 3;
// True if the package is privileged.
- optional bool is_priv_app = 4;
+ // Starting from Android 11, this boolean is not set and will always be false.
+ optional bool is_priv_app = 4 [deprecated = true];
}
/**
@@ -6716,6 +6717,9 @@
// For long-running operations, total duration of the operation
// while the app was in the background (only for trusted requests)
optional int64 trusted_background_duration_millis = 9;
+
+ // Whether AppOps is guarded by Runtime permission
+ optional bool is_runtime_permission = 10;
}
/**
diff --git a/core/TEST_MAPPING b/core/TEST_MAPPING
new file mode 100644
index 0000000..fd571c9
--- /dev/null
+++ b/core/TEST_MAPPING
@@ -0,0 +1,24 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.inputmethod"
+ },
+ {
+ "include-filter": "com.android.internal.inputmethod"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ],
+ "file_patterns": [
+ "core/java/com/android/internal/inputmethod/.*",
+ "core/java/android/view/inputmethod/.*",
+ "core/tests/coretests/src/android/view/inputmethod/.*",
+ "core/tests/coretests/src/com/android/internal/inputmethod/.*"
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1c8f494..46f88d5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1362,6 +1362,18 @@
}
@Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp,
+ Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, @Nullable Bundle initialExtras) {
+ int intAppOp = AppOpsManager.OP_NONE;
+ if (!TextUtils.isEmpty(receiverAppOp)) {
+ intAppOp = AppOpsManager.strOpToOp(receiverAppOp);
+ }
+ sendOrderedBroadcastAsUser(intent, getUser(), receiverPermission, intAppOp, options,
+ resultReceiver, scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
@Deprecated
public void sendStickyBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index d9b3d3b..1829f74 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -34,6 +34,7 @@
import android.app.slice.SliceManager;
import android.app.timedetector.TimeDetector;
import android.app.timezone.RulesManager;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
@@ -1125,6 +1126,14 @@
return new TimeDetector();
}});
+ registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
+ new CachedServiceFetcher<TimeZoneDetector>() {
+ @Override
+ public TimeZoneDetector createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ return new TimeZoneDetector();
+ }});
+
registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
new CachedServiceFetcher<PermissionManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 73980a5..47fd87d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6363,6 +6363,9 @@
/**
* Sets the device owner information to be shown on the lock screen.
* <p>
+ * Device owner information set using this method overrides any owner information manually set
+ * by the user and prevents the user from further changing it.
+ * <p>
* If the device owner information is {@code null} or empty then the device owner info is
* cleared and the user owner info is shown on the lock screen if it is set.
* <p>
@@ -6372,6 +6375,8 @@
* If the device owner information needs to be localized, it is the responsibility of the
* {@link DeviceAdminReceiver} to listen to the {@link Intent#ACTION_LOCALE_CHANGED} broadcast
* and set a new version of this string accordingly.
+ * <p>
+ * May be called by the device owner or the profile owner of an organization-owned device.
*
* @param admin The name of the admin component to check.
* @param info Device owner information which will be displayed instead of the user owner info.
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 25caaaa..93d1e71 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -34,6 +34,8 @@
import android.util.Log;
import android.util.Pair;
+import java.util.List;
+
/**
* The interface through which an application interacts with the Android backup service to
* request backup and restore operations.
@@ -948,6 +950,29 @@
return null;
}
+ /**
+ * Excludes keys from KV restore for a given package. The corresponding data will be excluded
+ * from the data set available the backup agent during restore. However, final list of keys
+ * that have been excluded will be passed to the agent to make it aware of the exclusions.
+ *
+ * @param packageName The name of the package for which to exclude keys.
+ * @param keys The list of keys to exclude.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.BACKUP)
+ public void excludeKeysFromRestore(@NonNull String packageName, @NonNull List<String> keys) {
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ sService.excludeKeysFromRestore(packageName, keys);
+ } catch (RemoteException e) {
+ Log.e(TAG, "excludeKeysFromRestore() couldn't connect");
+ }
+ }
+ }
+
/*
* We wrap incoming binder calls with a private class implementation that
* redirects them into main-thread actions. This serializes the backup
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 2dfaad7..099272d 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -710,4 +710,10 @@
*/
void setAncestralSerialNumber(in long ancestralSerialNumber);
+ /**
+ * Excludes keys from KV restore for a given package. The corresponding data will be excluded
+ * from the data set available the backup agent during restore. However, final list of keys
+ * that have been excluded will be passed to the agent to make it aware of the exclusions.
+ */
+ void excludeKeysFromRestore(String packageName, in List<String> keys);
}
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
new file mode 100644
index 0000000..260c7df
--- /dev/null
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.app.timezonedetector;
+
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+
+/**
+ * System private API to communicate with time zone detector service.
+ *
+ * <p>Used to provide information to the Time Zone Detector Service from other parts of the Android
+ * system that have access to time zone-related signals, e.g. telephony.
+ *
+ * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
+ * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
+ * for more complete documentation.
+ *
+ *
+ * {@hide}
+ */
+interface ITimeZoneDetectorService {
+ void suggestPhoneTimeZone(in PhoneTimeZoneSuggestion timeZoneSuggestion);
+}
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl
new file mode 100644
index 0000000..3ad903b
--- /dev/null
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.app.timezonedetector;
+
+parcelable PhoneTimeZoneSuggestion;
diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
new file mode 100644
index 0000000..e8162488
--- /dev/null
+++ b/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 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.app.timezonedetector;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A suggested time zone from a Phone-based signal, e.g. from MCC and NITZ information.
+ *
+ * @hide
+ */
+public final class PhoneTimeZoneSuggestion implements Parcelable {
+
+ @NonNull
+ public static final Creator<PhoneTimeZoneSuggestion> CREATOR =
+ new Creator<PhoneTimeZoneSuggestion>() {
+ public PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
+ return PhoneTimeZoneSuggestion.createFromParcel(in);
+ }
+
+ public PhoneTimeZoneSuggestion[] newArray(int size) {
+ return new PhoneTimeZoneSuggestion[size];
+ }
+ };
+
+ /**
+ * Creates an empty time zone suggestion, i.e. one that will cancel previous suggestions with
+ * the same {@code phoneId}.
+ */
+ @NonNull
+ public static PhoneTimeZoneSuggestion createEmptySuggestion(
+ int phoneId, @NonNull String debugInfo) {
+ return new Builder(phoneId).addDebugInfo(debugInfo).build();
+ }
+
+ @IntDef({ MATCH_TYPE_NA, MATCH_TYPE_NETWORK_COUNTRY_ONLY, MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
+ MATCH_TYPE_EMULATOR_ZONE_ID, MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MatchType {}
+
+ /** Used when match type is not applicable. */
+ public static final int MATCH_TYPE_NA = 0;
+
+ /**
+ * Only the network country is known.
+ */
+ public static final int MATCH_TYPE_NETWORK_COUNTRY_ONLY = 2;
+
+ /**
+ * Both the network county and offset were known.
+ */
+ public static final int MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET = 3;
+
+ /**
+ * The device is running in an emulator and an NITZ signal was simulated containing an
+ * Android extension with an explicit Olson ID.
+ */
+ public static final int MATCH_TYPE_EMULATOR_ZONE_ID = 4;
+
+ /**
+ * The phone is most likely running in a test network not associated with a country (this is
+ * distinct from the country just not being known yet).
+ * Historically, Android has just picked an arbitrary time zone with the correct offset when
+ * on a test network.
+ */
+ public static final int MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY = 5;
+
+ @IntDef({ QUALITY_NA, QUALITY_SINGLE_ZONE, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Quality {}
+
+ /** Used when quality is not applicable. */
+ public static final int QUALITY_NA = 0;
+
+ /** There is only one answer */
+ public static final int QUALITY_SINGLE_ZONE = 1;
+
+ /**
+ * There are multiple answers, but they all shared the same offset / DST state at the time
+ * the suggestion was created. i.e. it might be the wrong zone but the user won't notice
+ * immediately if it is wrong.
+ */
+ public static final int QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET = 2;
+
+ /**
+ * There are multiple answers with different offsets. The one given is just one possible.
+ */
+ public static final int QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS = 3;
+
+ /**
+ * The ID of the phone this suggestion is associated with. For multiple-sim devices this
+ * helps to establish origin so filtering / stickiness can be implemented.
+ */
+ private final int mPhoneId;
+
+ /**
+ * The suggestion. {@code null} means there is no current suggestion and any previous suggestion
+ * should be forgotten.
+ */
+ private final String mZoneId;
+
+ /**
+ * The type of "match" used to establish the time zone.
+ */
+ @MatchType
+ private final int mMatchType;
+
+ /**
+ * A measure of the quality of the time zone suggestion, i.e. how confident one could be in
+ * it.
+ */
+ @Quality
+ private final int mQuality;
+
+ /**
+ * Free-form debug information about how the signal was derived. Used for debug only,
+ * intentionally not used in equals(), etc.
+ */
+ private List<String> mDebugInfo;
+
+ private PhoneTimeZoneSuggestion(Builder builder) {
+ mPhoneId = builder.mPhoneId;
+ mZoneId = builder.mZoneId;
+ mMatchType = builder.mMatchType;
+ mQuality = builder.mQuality;
+ mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) {
+ // Use the Builder so we get validation during build().
+ int phoneId = in.readInt();
+ PhoneTimeZoneSuggestion suggestion = new Builder(phoneId)
+ .setZoneId(in.readString())
+ .setMatchType(in.readInt())
+ .setQuality(in.readInt())
+ .build();
+ List<String> debugInfo = in.readArrayList(PhoneTimeZoneSuggestion.class.getClassLoader());
+ if (debugInfo != null) {
+ suggestion.addDebugInfo(debugInfo);
+ }
+ return suggestion;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPhoneId);
+ dest.writeString(mZoneId);
+ dest.writeInt(mMatchType);
+ dest.writeInt(mQuality);
+ dest.writeList(mDebugInfo);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public int getPhoneId() {
+ return mPhoneId;
+ }
+
+ @Nullable
+ public String getZoneId() {
+ return mZoneId;
+ }
+
+ @MatchType
+ public int getMatchType() {
+ return mMatchType;
+ }
+
+ @Quality
+ public int getQuality() {
+ return mQuality;
+ }
+
+ @NonNull
+ public List<String> getDebugInfo() {
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(@NonNull String debugInfo) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.add(debugInfo);
+ }
+
+ /**
+ * Associates information with the instance that can be useful for debugging / logging. The
+ * information is present in {@link #toString()} but is not considered for
+ * {@link #equals(Object)} and {@link #hashCode()}.
+ */
+ public void addDebugInfo(@NonNull List<String> debugInfo) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>(debugInfo.size());
+ }
+ mDebugInfo.addAll(debugInfo);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o;
+ return mPhoneId == that.mPhoneId
+ && mMatchType == that.mMatchType
+ && mQuality == that.mQuality
+ && Objects.equals(mZoneId, that.mZoneId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPhoneId, mZoneId, mMatchType, mQuality);
+ }
+
+ @Override
+ public String toString() {
+ return "PhoneTimeZoneSuggestion{"
+ + "mPhoneId=" + mPhoneId
+ + ", mZoneId='" + mZoneId + '\''
+ + ", mMatchType=" + mMatchType
+ + ", mQuality=" + mQuality
+ + ", mDebugInfo=" + mDebugInfo
+ + '}';
+ }
+
+ /**
+ * Builds {@link PhoneTimeZoneSuggestion} instances.
+ *
+ * @hide
+ */
+ public static class Builder {
+ private final int mPhoneId;
+ private String mZoneId;
+ @MatchType private int mMatchType;
+ @Quality private int mQuality;
+ private List<String> mDebugInfo;
+
+ public Builder(int phoneId) {
+ mPhoneId = phoneId;
+ }
+
+ /** Returns the builder for call chaining. */
+ public Builder setZoneId(String zoneId) {
+ mZoneId = zoneId;
+ return this;
+ }
+
+ /** Returns the builder for call chaining. */
+ public Builder setMatchType(@MatchType int matchType) {
+ mMatchType = matchType;
+ return this;
+ }
+
+ /** Returns the builder for call chaining. */
+ public Builder setQuality(@Quality int quality) {
+ mQuality = quality;
+ return this;
+ }
+
+ /** Returns the builder for call chaining. */
+ public Builder addDebugInfo(@NonNull String debugInfo) {
+ if (mDebugInfo == null) {
+ mDebugInfo = new ArrayList<>();
+ }
+ mDebugInfo.add(debugInfo);
+ return this;
+ }
+
+ /**
+ * Performs basic structural validation of this instance. e.g. Are all the fields populated
+ * that must be? Are the enum ints set to valid values?
+ */
+ void validate() {
+ int quality = mQuality;
+ int matchType = mMatchType;
+ if (mZoneId == null) {
+ if (quality != QUALITY_NA || matchType != MATCH_TYPE_NA) {
+ throw new RuntimeException("Invalid quality or match type for null zone ID."
+ + " quality=" + quality + ", matchType=" + matchType);
+ }
+ } else {
+ boolean qualityValid = (quality == QUALITY_SINGLE_ZONE
+ || quality == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET
+ || quality == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ boolean matchTypeValid = (matchType == MATCH_TYPE_NETWORK_COUNTRY_ONLY
+ || matchType == MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET
+ || matchType == MATCH_TYPE_EMULATOR_ZONE_ID
+ || matchType == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY);
+ if (!qualityValid || !matchTypeValid) {
+ throw new RuntimeException("Invalid quality or match type with zone ID."
+ + " quality=" + quality + ", matchType=" + matchType);
+ }
+ }
+ }
+
+ /** Returns the {@link PhoneTimeZoneSuggestion}. */
+ public PhoneTimeZoneSuggestion build() {
+ validate();
+ return new PhoneTimeZoneSuggestion(this);
+ }
+ }
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
new file mode 100644
index 0000000..909cbc2
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -0,0 +1,59 @@
+/*
+ * 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.app.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
+
+/**
+ * The interface through which system components can send signals to the TimeZoneDetectorService.
+ * @hide
+ */
+@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
+public final class TimeZoneDetector {
+ private static final String TAG = "timezonedetector.TimeZoneDetector";
+ private static final boolean DEBUG = false;
+
+ private final ITimeZoneDetectorService mITimeZoneDetectorService;
+
+ public TimeZoneDetector() throws ServiceNotFoundException {
+ mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
+ }
+
+ /**
+ * Suggests the current time zone to the detector. The detector may ignore the signal if better
+ * signals are available such as those that come from more reliable sources or were
+ * determined more recently.
+ */
+ public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ if (DEBUG) {
+ Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
+ }
+ try {
+ mITimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4a0fc66..341b520 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2484,6 +2484,48 @@
}
/**
+ * Version of
+ * {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
+ * Bundle)} that allows you to specify the App Op to enforce restrictions on which receivers
+ * the broadcast will be sent to as well as supply an optional sending options
+ *
+ * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast.
+ * @param receiverPermission String naming a permissions that
+ * a receiver must hold in order to receive your broadcast.
+ * If null, no permission is required.
+ * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is
+ * required. If both receiverAppOp and receiverPermission are non-null,
+ * a receiver must have both of them to
+ * receive the broadcast
+ * @param options (optional) Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ * @param resultReceiver Your own BroadcastReceiver to treat as the final
+ * receiver of the broadcast.
+ * @param scheduler A custom Handler with which to schedule the
+ * resultReceiver callback; if null it will be
+ * scheduled in the Context's main thread.
+ * @param initialCode An initial value for the result code. Often
+ * Activity.RESULT_OK.
+ * @param initialData An initial value for the result data. Often
+ * null.
+ * @param initialExtras An initial value for the result extras. Often
+ * null.
+ *
+ * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+ * @see android.app.BroadcastOptions
+ */
+ public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+ @Nullable String receiverPermission, @Nullable String receiverAppOp,
+ @Nullable Bundle options, @Nullable BroadcastReceiver resultReceiver,
+ @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+ @Nullable Bundle initialExtras) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
* Intent you are sending stays around after the broadcast is complete,
* so that others can quickly retrieve that data through the return
@@ -3383,7 +3425,9 @@
CROSS_PROFILE_APPS_SERVICE,
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
+ //@hide: TIME_ZONE_DETECTOR_SERVICE,
PERMISSION_SERVICE,
+ INCREMENTAL_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -4835,7 +4879,7 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
- * {@link android.app.timedetector.ITimeDetectorService}.
+ * {@link android.app.timedetector.TimeDetector}.
* @hide
*
* @see #getSystemService(String)
@@ -4843,6 +4887,15 @@
public static final String TIME_DETECTOR_SERVICE = "time_detector";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.app.timezonedetector.TimeZoneDetector}.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+
+ /**
* Binder service name for {@link AppBindingService}.
* @hide
*/
@@ -4917,6 +4970,13 @@
public static final String APP_INTEGRITY_SERVICE = "app_integrity";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.os.incremental.IncrementalManager}.
+ * @hide
+ */
+ public static final String INCREMENTAL_SERVICE = "incremental";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e44d6ae..d6442e2 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -590,6 +590,16 @@
}
@Override
+ public void sendOrderedBroadcast(@RequiresPermission @NonNull Intent intent,
+ @Nullable String receiverPermission, @Nullable String receiverAppOp,
+ @Nullable Bundle options, @Nullable BroadcastReceiver resultReceiver,
+ @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+ @Nullable Bundle initialExtras) {
+ mBase.sendOrderedBroadcast(intent, receiverPermission, receiverAppOp, options,
+ resultReceiver, scheduler, initialCode, initialData, initialExtras);
+ }
+
+ @Override
@Deprecated
public void sendStickyBroadcast(Intent intent) {
mBase.sendStickyBroadcast(intent);
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index 143467b..dddb64d 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -191,18 +191,12 @@
83 * httpProxy.hashCode();
}
- /**
- * Implement the Parcelable interface
- * @hide
- */
+ /** Implement the Parcelable interface */
public int describeContents() {
return 0;
}
- /**
- * Implement the Parcelable interface
- * @hide
- */
+ /** Implement the Parcelable interface */
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(ipAssignment.name());
dest.writeString(proxySettings.name());
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 0706e75..8e18341 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -762,10 +762,7 @@
* Returns the NAT64 prefix in use on this link, if any.
*
* @return the NAT64 prefix or {@code null}.
- * @hide
*/
- @SystemApi
- @TestApi
public @Nullable IpPrefix getNat64Prefix() {
return mNat64Prefix;
}
@@ -777,10 +774,7 @@
* 128-bit IPv6 address) are supported or {@code null} for no prefix.
*
* @param prefix the NAT64 prefix.
- * @hide
*/
- @SystemApi
- @TestApi
public void setNat64Prefix(@Nullable IpPrefix prefix) {
if (prefix != null && prefix.getPrefixLength() != 96) {
throw new IllegalArgumentException("Only 96-bit prefixes are supported: " + prefix);
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ec39199..15ff69e 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -505,6 +505,19 @@
public static final native void restoreCallingWorkSource(long token);
/**
+ * Mark as being built with VINTF-level stability promise. This API should
+ * only ever be invoked by the build system. It means that the interface
+ * represented by this binder is guaranteed to be kept stable for several
+ * years, and the build system also keeps snapshots of these APIs and
+ * invokes the AIDL compiler to make sure that these snapshots are
+ * backwards compatible. Instead of using this API, use an @VintfStability
+ * interface.
+ *
+ * @hide
+ */
+ public final native void markVintfStability();
+
+ /**
* Flush any Binder commands pending in the current thread to the kernel
* driver. This can be
* useful to call before performing an operation that may block for a long
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 89ccef6..6eaea99 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1084,7 +1084,7 @@
return result == 0;
}
- final String system = SystemProperties.get("ro.build.fingerprint");
+ final String system = SystemProperties.get("ro.system.build.fingerprint");
final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint");
final String requiredBootloader = SystemProperties.get("ro.build.expect.bootloader");
@@ -1094,7 +1094,7 @@
TelephonyProperties.baseband_version(), "");
if (TextUtils.isEmpty(system)) {
- Slog.e(TAG, "Required ro.build.fingerprint is empty!");
+ Slog.e(TAG, "Required ro.system.build.fingerprint is empty!");
return false;
}
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index 4d9ebc2..5b715c0 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -32,7 +32,7 @@
/**
* Monitors files (using <a href="http://en.wikipedia.org/wiki/Inotify">inotify</a>)
- * to fire an event after files are accessed or changed by by any process on
+ * to fire an event after files are accessed or changed by any process on
* the device (including this one). FileObserver is an abstract class;
* subclasses must implement the event handler {@link #onEvent(int, String)}.
*
diff --git a/core/java/android/os/ResultReceiver.aidl b/core/java/android/os/ResultReceiver.aidl
index 28ce6d5..9fd5bc9 100644
--- a/core/java/android/os/ResultReceiver.aidl
+++ b/core/java/android/os/ResultReceiver.aidl
@@ -2,19 +2,19 @@
**
** Copyright 2007, 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
+** 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
+** 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
+** 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.os;
-parcelable ResultReceiver;
+@JavaOnlyStableParcelable parcelable ResultReceiver;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index ce91c50..74340f0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -46,6 +46,7 @@
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.view.WindowManager.LayoutParams;
@@ -486,13 +487,13 @@
*
* <p>This user restriction is different from {@link #DISALLOW_SHARE_LOCATION},
* as the device owner or profile owner can still enable or disable location mode via
- * {@link DevicePolicyManager#setSecureSetting} when this restriction is on.
+ * {@link DevicePolicyManager#setLocationEnabled} when this restriction is on.
*
* <p>The default value is <code>false</code>.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
- * @see android.location.LocationManager#isProviderEnabled(String)
+ * @see LocationManager#isLocationEnabled()
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalManager.aidl
similarity index 77%
rename from core/java/android/os/incremental/IIncrementalService.aidl
rename to core/java/android/os/incremental/IIncrementalManager.aidl
index 1c832ca..d6446d4 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalManager.aidl
@@ -19,7 +19,7 @@
import android.os.incremental.IncrementalDataLoaderParamsParcel;
/** @hide */
-interface IIncrementalService {
+interface IIncrementalManager {
/**
* A set of flags for the |createMode| parameters when creating a new Incremental storage.
*/
@@ -53,6 +53,12 @@
int makeDirectory(int storageId, in @utf8InCpp String pathUnderStorage);
/**
+ * Recursively creates a directory under a storage. The target directory is specified by its relative path under the storage.
+ * All the parent directories of the target directory will be created if they do not exist already.
+ */
+ int makeDirectories(int storageId, in @utf8InCpp String pathUnderStorage);
+
+ /**
* Creates a file under a storage, specifying its name, size and metadata.
*/
int makeFile(int storageId, in @utf8InCpp String pathUnderStorage, long size, in byte[] metadata);
@@ -64,10 +70,12 @@
int makeFileFromRange(int storageId, in @utf8InCpp String targetPathUnderStorage, in @utf8InCpp String sourcePathUnderStorage, long start, long end);
/**
- * Creates a hard link between two files in a storage.
- * Both source and destination are specified by relative paths under storage.
+ * Creates a hard link between two files in two storage instances.
+ * Source and dest specified by parent storage IDs and their relative paths under the storage.
+ * The source and dest storage instances should be in the same fs mount.
+ * Note: destStorageId can be the same as sourceStorageId.
*/
- int makeLink(int storageId, in @utf8InCpp String sourcePathUnderStorage, in @utf8InCpp String destPathUnderStorage);
+ int makeLink(int sourceStorageId, in @utf8InCpp String sourcePathUnderStorage, int destStorageId, in @utf8InCpp String destPathUnderStorage);
/**
* Deletes a hard link in a storage, specified by the relative path of the link target under storage.
@@ -85,12 +93,12 @@
byte[] getFileMetadata(int storageId, in @utf8InCpp String pathUnderStorage);
/**
- * Returns the list of file paths under a storage.
- */
- @utf8InCpp String[] getFileList(int storageId);
-
- /**
* Starts loading data for a storage.
*/
boolean startLoading(int storageId);
+
+ /**
+ * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
+ */
+ void deleteStorage(int storageId);
}
diff --git a/core/java/android/os/incremental/IncrementalDataLoaderParams.java b/core/java/android/os/incremental/IncrementalDataLoaderParams.java
new file mode 100644
index 0000000..701f1cc
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalDataLoaderParams.java
@@ -0,0 +1,84 @@
+/*
+ * 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.os.incremental;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * This class represents the parameters used to configure an Incremental Data Loader.
+ * Hide for now.
+ * @hide
+ */
+public class IncrementalDataLoaderParams {
+ @NonNull private final IncrementalDataLoaderParamsParcel mData;
+
+ public IncrementalDataLoaderParams(@NonNull String url, @NonNull String packageName,
+ @Nullable Map<String, ParcelFileDescriptor> namedFds) {
+ IncrementalDataLoaderParamsParcel data = new IncrementalDataLoaderParamsParcel();
+ data.staticArgs = url;
+ data.packageName = packageName;
+ if (namedFds == null || namedFds.isEmpty()) {
+ data.dynamicArgs = new NamedParcelFileDescriptor[0];
+ } else {
+ data.dynamicArgs = new NamedParcelFileDescriptor[namedFds.size()];
+ int i = 0;
+ for (Map.Entry<String, ParcelFileDescriptor> namedFd : namedFds.entrySet()) {
+ data.dynamicArgs[i] = new NamedParcelFileDescriptor();
+ data.dynamicArgs[i].name = namedFd.getKey();
+ data.dynamicArgs[i].fd = namedFd.getValue();
+ i += 1;
+ }
+ }
+ mData = data;
+ }
+
+ public IncrementalDataLoaderParams(@NonNull IncrementalDataLoaderParamsParcel data) {
+ mData = data;
+ }
+
+ /**
+ * @return static server's URL
+ */
+ public final @NonNull String getStaticArgs() {
+ return mData.staticArgs;
+ }
+
+ /**
+ * @return data loader's package name
+ */
+ public final @NonNull String getPackageName() {
+ return mData.packageName;
+ }
+
+ public final @NonNull IncrementalDataLoaderParamsParcel getData() {
+ return mData;
+ }
+
+ /**
+ * @return data loader's dynamic arguments such as file descriptors
+ */
+ public final @NonNull Map<String, ParcelFileDescriptor> getDynamicArgs() {
+ return Arrays.stream(mData.dynamicArgs).collect(
+ Collectors.toMap(p->p.name, p->p.fd));
+ }
+}
diff --git a/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl b/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl
index 50c28f0..cd988dc 100644
--- a/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl
+++ b/core/java/android/os/incremental/IncrementalDataLoaderParamsParcel.aidl
@@ -23,7 +23,7 @@
* @hide
*/
parcelable IncrementalDataLoaderParamsParcel {
- @utf8InCpp String staticUri;
@utf8InCpp String packageName;
+ @utf8InCpp String staticArgs;
NamedParcelFileDescriptor[] dynamicArgs;
}
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
new file mode 100644
index 0000000..5aabf86
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -0,0 +1,327 @@
+/*
+ * 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.os.incremental;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Provides operations to open or create an IncrementalStorage, using IIncrementalManager service.
+ * Example Usage:
+ *
+ * <blockquote><pre>
+ * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_MANAGER);
+ * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
+ * </pre></blockquote>
+ *
+ * @hide
+ */
+@SystemService(Context.INCREMENTAL_SERVICE)
+public class IncrementalManager {
+ private static final String TAG = "IncrementalManager";
+ private final IIncrementalManager mService;
+ @GuardedBy("mStorages")
+ private final SparseArray<IncrementalStorage> mStorages = new SparseArray<>();
+
+ public static final int CREATE_MODE_TEMPORARY_BIND =
+ IIncrementalManager.CREATE_MODE_TEMPORARY_BIND;
+ public static final int CREATE_MODE_PERMANENT_BIND =
+ IIncrementalManager.CREATE_MODE_PERMANENT_BIND;
+ public static final int CREATE_MODE_CREATE =
+ IIncrementalManager.CREATE_MODE_CREATE;
+ public static final int CREATE_MODE_OPEN_EXISTING =
+ IIncrementalManager.CREATE_MODE_OPEN_EXISTING;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"CREATE_MODE_"}, value = {
+ CREATE_MODE_TEMPORARY_BIND,
+ CREATE_MODE_PERMANENT_BIND,
+ CREATE_MODE_CREATE,
+ CREATE_MODE_OPEN_EXISTING,
+ })
+ public @interface CreateMode {
+ }
+
+ public IncrementalManager(@NonNull IIncrementalManager is) {
+ mService = is;
+ }
+
+ /**
+ * Returns a storage object given a storage ID.
+ *
+ * @param storageId The storage ID to identify the storage object.
+ * @return IncrementalStorage object corresponding to storage ID.
+ */
+ @Nullable
+ public IncrementalStorage getStorage(int storageId) {
+ synchronized (mStorages) {
+ return mStorages.get(storageId);
+ }
+ }
+
+ /**
+ * Opens or create an Incremental File System mounted directory and returns an
+ * IncrementalStorage object.
+ *
+ * @param path Absolute path to mount Incremental File System on.
+ * @param params IncrementalDataLoaderParams object to configure data loading.
+ * @param createMode Mode for opening an old Incremental File System mount or
+ * creating a new mount.
+ * @param autoStartDataLoader Set true to immediately start data loader after creating storage.
+ * @return IncrementalStorage object corresponding to the mounted directory.
+ */
+ @Nullable
+ public IncrementalStorage createStorage(@NonNull String path,
+ @NonNull IncrementalDataLoaderParams params, @CreateMode int createMode,
+ boolean autoStartDataLoader) {
+ try {
+ final int id = mService.createStorage(path, params.getData(), createMode);
+ if (id < 0) {
+ return null;
+ }
+ final IncrementalStorage storage = new IncrementalStorage(mService, id);
+ synchronized (mStorages) {
+ mStorages.put(id, storage);
+ }
+ if (autoStartDataLoader) {
+ storage.startLoading();
+ }
+ return storage;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Opens an existing Incremental File System mounted directory and returns an
+ * IncrementalStorage object.
+ *
+ * @param path Absolute target path that Incremental File System has been mounted on.
+ * @return IncrementalStorage object corresponding to the mounted directory.
+ */
+ @Nullable
+ public IncrementalStorage openStorage(@NonNull String path) {
+ try {
+ final int id = mService.openStorage(path);
+ if (id < 0) {
+ return null;
+ }
+ final IncrementalStorage storage = new IncrementalStorage(mService, id);
+ synchronized (mStorages) {
+ mStorages.put(id, storage);
+ }
+ return storage;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Opens or creates an IncrementalStorage that is linked to another IncrementalStorage.
+ *
+ * @return IncrementalStorage object corresponding to the linked storage.
+ */
+ @Nullable
+ public IncrementalStorage createStorage(@NonNull String path,
+ @NonNull IncrementalStorage linkedStorage, @CreateMode int createMode) {
+ try {
+ final int id = mService.createLinkedStorage(path, linkedStorage.getId(), createMode);
+ if (id < 0) {
+ return null;
+ }
+ final IncrementalStorage storage = new IncrementalStorage(mService, id);
+ synchronized (mStorages) {
+ mStorages.put(id, storage);
+ }
+ return storage;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Iterates through path parents to find the base dir of an Incremental Storage.
+ *
+ * @param file Target file to search storage for.
+ * @return Absolute path which is a bind-mount point of Incremental File System.
+ */
+ private Path getStoragePathForFile(File file) {
+ File currentPath = new File(file.getParent());
+ while (currentPath.getParent() != null) {
+ IncrementalStorage storage = openStorage(currentPath.getAbsolutePath());
+ if (storage != null) {
+ return currentPath.toPath();
+ }
+ currentPath = new File(currentPath.getParent());
+ }
+ return null;
+ }
+
+ /**
+ * Renames an Incremental path to a new path. If source path is a file, make a link from the old
+ * Incremental file to the new one. If source path is a dir, unbind old dir from Incremental
+ * Storage and bind the new one.
+ * <ol>
+ * <li> For renaming a dir, dest dir will be created if not exists, and does not need to
+ * be on the same Incremental storage as the source. </li>
+ * <li> For renaming a file, dest file must be on the same Incremental storage as source.
+ * </li>
+ * </ol>
+ *
+ * @param sourcePath Absolute path to the source. Should be the same type as the destPath
+ * (file or dir). Expected to already exist and is an Incremental path.
+ * @param destPath Absolute path to the destination.
+ * @throws IllegalArgumentException when 1) source does not exist,
+ * or 2) source and dest type mismatch (one is file and the other is dir),
+ * or 3) source path is not on Incremental File System,
+ * @throws IOException when 1) cannot find the root path of the Incremental storage of source,
+ * or 2) cannot retrieve the Incremental storage instance of the source,
+ * or 3) renaming a file, but dest is not on the same Incremental Storage,
+ * or 4) renaming a dir, dest dir does not exist but fails to be created.
+ *
+ * TODO(b/136132412): add unit tests
+ */
+ public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
+ final File source = new File(sourcePath);
+ final File dest = new File(destPath);
+ if (!source.exists()) {
+ throw new IllegalArgumentException("Path not exist: " + sourcePath);
+ }
+ if (dest.exists()) {
+ throw new IllegalArgumentException("Target path already exists: " + destPath);
+ }
+ if (source.isDirectory() && dest.exists() && dest.isFile()) {
+ throw new IllegalArgumentException(
+ "Trying to rename a dir but destination is a file: " + destPath);
+ }
+ if (source.isFile() && dest.exists() && dest.isDirectory()) {
+ throw new IllegalArgumentException(
+ "Trying to rename a file but destination is a dir: " + destPath);
+ }
+ if (!isIncrementalPath(sourcePath)) {
+ throw new IllegalArgumentException("Not an Incremental path: " + sourcePath);
+ }
+
+ Path storagePath = Paths.get(sourcePath);
+ if (source.isFile()) {
+ storagePath = getStoragePathForFile(source);
+ }
+ if (storagePath == null || storagePath.toAbsolutePath() == null) {
+ throw new IOException("Invalid source storage path for: " + sourcePath);
+ }
+ final IncrementalStorage storage = openStorage(storagePath.toAbsolutePath().toString());
+ if (storage == null) {
+ throw new IOException("Failed to retrieve storage from Incremental Service.");
+ }
+ if (source.isFile()) {
+ renameFile(storage, storagePath, source, dest);
+ } else {
+ renameDir(storage, storagePath, source, dest);
+ }
+ }
+
+ private void renameFile(IncrementalStorage storage, Path storagePath,
+ File source, File dest) throws IOException {
+ Path sourcePath = source.toPath();
+ Path destPath = dest.toPath();
+ if (!sourcePath.startsWith(storagePath)) {
+ throw new IOException("Path: " + source.getAbsolutePath() + " is not on storage at: "
+ + storagePath.toString());
+ }
+ if (!destPath.startsWith(storagePath)) {
+ throw new IOException("Path: " + dest.getAbsolutePath() + " is not on storage at: "
+ + storagePath.toString());
+ }
+ final Path sourceRelativePath = storagePath.relativize(sourcePath);
+ final Path destRelativePath = storagePath.relativize(destPath);
+ storage.moveFile(sourceRelativePath.toString(), destRelativePath.toString());
+
+ }
+
+ private void renameDir(IncrementalStorage storage, Path storagePath,
+ File source, File dest) throws IOException {
+ Path destPath = dest.toPath();
+ boolean usedMkdir = false;
+ try {
+ Os.mkdir(dest.getAbsolutePath(), 0755);
+ usedMkdir = true;
+ } catch (ErrnoException e) {
+ // Traditional mkdir fails but maybe we can create it on Incremental File System if
+ // the dest path is on the same Incremental storage as the source.
+ if (destPath.startsWith(storagePath)) {
+ storage.makeDirectories(storagePath.relativize(destPath).toString());
+ } else {
+ throw new IOException("Failed to create directory: " + dest.getAbsolutePath(), e);
+ }
+ }
+ try {
+ storage.moveDir(source.getAbsolutePath(), dest.getAbsolutePath());
+ } catch (Exception ex) {
+ if (usedMkdir) {
+ try {
+ Os.remove(dest.getAbsolutePath());
+ } catch (ErrnoException ignored) {
+ }
+ }
+ throw new IOException(
+ "Failed to move " + source.getAbsolutePath() + " to " + dest.getAbsolutePath());
+ }
+ }
+
+ /**
+ * Closes a storage specified by the absolute path. If the path is not Incremental, do nothing.
+ * Unbinds the target dir and deletes the corresponding storage instance.
+ */
+ public void closeStorage(@NonNull String path) {
+ try {
+ final int id = mService.openStorage(path);
+ if (id < 0) {
+ return;
+ }
+ mService.deleteStorage(id);
+ synchronized (mStorages) {
+ mStorages.remove(id);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Checks if path is mounted on Incremental File System.
+ */
+ public static boolean isIncrementalPath(@NonNull String path) {
+ // TODO(b/136132412): implement native method
+ return false;
+ }
+}
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
new file mode 100644
index 0000000..2bf89ed
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -0,0 +1,346 @@
+/*
+ * 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.os.incremental;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Provides operations on an Incremental File System directory, using IncrementalService. Example
+ * usage:
+ *
+ * <blockquote><pre>
+ * IncrementalManager manager = (IncrementalManager) getSystemService(Context.INCREMENTAL_MANAGER);
+ * IncrementalStorage storage = manager.openStorage("/path/to/incremental/dir");
+ * storage.makeDirectory("subdir");
+ * </pre></blockquote>
+ *
+ * @hide
+ */
+public final class IncrementalStorage {
+ private static final String TAG = "IncrementalStorage";
+ private final int mId;
+ private final IIncrementalManager mService;
+
+
+ public IncrementalStorage(@NonNull IIncrementalManager is, int id) {
+ mService = is;
+ mId = id;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Temporarily bind-mounts the current storage directory to a target directory. The bind-mount
+ * will NOT be preserved between device reboots.
+ *
+ * @param targetPath Absolute path to the target directory.
+ */
+ public void bind(@NonNull String targetPath) throws IOException {
+ bind("", targetPath);
+ }
+
+ /**
+ * Temporarily bind-mounts a subdir under the current storage directory to a target directory.
+ * The bind-mount will NOT be preserved between device reboots.
+ *
+ * @param sourcePathUnderStorage Source path as a relative path under current storage
+ * directory.
+ * @param targetPath Absolute path to the target directory.
+ */
+ public void bind(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
+ throws IOException {
+ try {
+ int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
+ IIncrementalManager.BIND_TEMPORARY);
+ if (res < 0) {
+ throw new IOException("bind() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Permanently bind-mounts the current storage directory to a target directory. The bind-mount
+ * WILL be preserved between device reboots.
+ *
+ * @param targetPath Absolute path to the target directory.
+ */
+ public void bindPermanent(@NonNull String targetPath) throws IOException {
+ bindPermanent("", targetPath);
+ }
+
+ /**
+ * Permanently bind-mounts a subdir under the current storage directory to a target directory.
+ * The bind-mount WILL be preserved between device reboots.
+ *
+ * @param sourcePathUnderStorage Relative path under the current storage directory.
+ * @param targetPath Absolute path to the target directory.
+ */
+ public void bindPermanent(@NonNull String sourcePathUnderStorage, @NonNull String targetPath)
+ throws IOException {
+ try {
+ int res = mService.makeBindMount(mId, sourcePathUnderStorage, targetPath,
+ IIncrementalManager.BIND_PERMANENT);
+ if (res < 0) {
+ throw new IOException("bind() permanent failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unbinds a bind mount.
+ *
+ * @param targetPath Absolute path to the target directory.
+ */
+ public void unBind(@NonNull String targetPath) throws IOException {
+ try {
+ int res = mService.deleteBindMount(mId, targetPath);
+ if (res < 0) {
+ throw new IOException("unbind() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a sub-directory under the current storage directory.
+ *
+ * @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir"
+ */
+ public void makeDirectory(@NonNull String pathUnderStorage) throws IOException {
+ try {
+ int res = mService.makeDirectory(mId, pathUnderStorage);
+ if (res < 0) {
+ throw new IOException("makeDirectory() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a sub-directory under the current storage directory. If its parent dirs do not exist,
+ * create the parent dirs as well.
+ *
+ * @param pathUnderStorage Relative path of the sub-directory, e.g., "subdir/subsubdir"
+ */
+ public void makeDirectories(@NonNull String pathUnderStorage) throws IOException {
+ try {
+ int res = mService.makeDirectories(mId, pathUnderStorage);
+ if (res < 0) {
+ throw new IOException("makeDirectory() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a file under the current storage directory.
+ *
+ * @param pathUnderStorage Relative path of the new file.
+ * @param size Size of the new file in bytes.
+ * @param metadata Metadata bytes.
+ */
+ public void makeFile(@NonNull String pathUnderStorage, long size,
+ @Nullable byte[] metadata) throws IOException {
+ try {
+ int res = mService.makeFile(mId, pathUnderStorage, size, metadata);
+ if (res < 0) {
+ throw new IOException("makeFile() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a file in Incremental storage. The content of the file is mapped from a range inside
+ * a source file in the same storage.
+ *
+ * @param destRelativePath Target relative path under storage.
+ * @param sourceRelativePath Source relative path under storage.
+ * @param rangeStart Starting offset (in bytes) in the source file.
+ * @param rangeEnd Ending offset (in bytes) in the source file.
+ */
+ public void makeFileFromRange(@NonNull String destRelativePath,
+ @NonNull String sourceRelativePath, long rangeStart, long rangeEnd) throws IOException {
+ try {
+ int res = mService.makeFileFromRange(mId, destRelativePath, sourceRelativePath,
+ rangeStart, rangeEnd);
+ if (res < 0) {
+ throw new IOException("makeFileFromRange() failed, errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a hard-link between two paths, which can be under different storages but in the same
+ * Incremental File System.
+ *
+ * @param sourcePathUnderStorage The relative path of the source.
+ * @param destStorage The target storage of the link target.
+ * @param destPathUnderStorage The relative path of the target.
+ */
+ public void makeLink(@NonNull String sourcePathUnderStorage, IncrementalStorage destStorage,
+ @NonNull String destPathUnderStorage) throws IOException {
+ try {
+ int res = mService.makeLink(mId, sourcePathUnderStorage, destStorage.getId(),
+ destPathUnderStorage);
+ if (res < 0) {
+ throw new IOException("makeLink() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Deletes a hard-link under the current storage directory.
+ *
+ * @param pathUnderStorage The relative path of the target.
+ */
+ public void unlink(@NonNull String pathUnderStorage) throws IOException {
+ try {
+ int res = mService.unlink(mId, pathUnderStorage);
+ if (res < 0) {
+ throw new IOException("unlink() failed with errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Rename an old file name to a new file name under the current storage directory.
+ *
+ * @param sourcePathUnderStorage Old file path as a relative path to the storage directory.
+ * @param destPathUnderStorage New file path as a relative path to the storage directory.
+ */
+ public void moveFile(@NonNull String sourcePathUnderStorage,
+ @NonNull String destPathUnderStorage) throws IOException {
+ try {
+ int res = mService.makeLink(mId, sourcePathUnderStorage, mId, destPathUnderStorage);
+ if (res < 0) {
+ throw new IOException("moveFile() failed at makeLink(), errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ try {
+ mService.unlink(mId, sourcePathUnderStorage);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Move a directory, which is bind-mounted to a given storage, to a new location. The bind mount
+ * will be persistent between reboots.
+ *
+ * @param sourcePath The old path of the directory as an absolute path.
+ * @param destPath The new path of the directory as an absolute path, expected to already
+ * exist.
+ */
+ public void moveDir(@NonNull String sourcePath, @NonNull String destPath) throws IOException {
+ if (!new File(destPath).exists()) {
+ throw new IOException("moveDir() requires that destination dir already exists.");
+ }
+ try {
+ int res = mService.makeBindMount(mId, "", destPath, IIncrementalManager.BIND_PERMANENT);
+ if (res < 0) {
+ throw new IOException("moveDir() failed at making bind mount, errno " + -res);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ try {
+ mService.deleteBindMount(mId, sourcePath);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Checks whether a file under the current storage directory is fully loaded.
+ *
+ * @param pathUnderStorage The relative path of the file.
+ * @return True if the file is fully loaded.
+ */
+ public boolean isFileFullyLoaded(@NonNull String pathUnderStorage) {
+ return isFileRangeLoaded(pathUnderStorage, 0, -1);
+ }
+
+ /**
+ * Checks whether a range in a file if loaded.
+ *
+ * @param pathUnderStorage The relative path of the file.
+ * @param start The starting offset of the range.
+ * @param end The ending offset of the range.
+ * @return True if the file is fully loaded.
+ */
+ public boolean isFileRangeLoaded(@NonNull String pathUnderStorage, long start, long end) {
+ try {
+ return mService.isFileRangeLoaded(mId, pathUnderStorage, start, end);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
+ /**
+ * Returns the metadata object of an IncFs File.
+ *
+ * @param pathUnderStorage The relative path of the file.
+ * @return Byte array that contains metadata bytes.
+ */
+ @Nullable
+ public byte[] getFileMetadata(@NonNull String pathUnderStorage) {
+ try {
+ return mService.getFileMetadata(mId, pathUnderStorage);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
+ * Informs the data loader service associated with the current storage to start data loader
+ *
+ * @return True if data loader is successfully started.
+ */
+ public boolean startLoading() {
+ try {
+ return mService.startLoading(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 703b86d..f19ef65 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -89,7 +89,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -4953,7 +4952,6 @@
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED);
MOVED_TO_GLOBAL.add(Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET);
MOVED_TO_GLOBAL.add(Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON);
- MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_ENABLE);
MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_TIMEOUT);
MOVED_TO_GLOBAL.add(Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE);
MOVED_TO_GLOBAL.add(Settings.Global.DATA_STALL_ALARM_NON_AGGRESSIVE_DELAY_IN_MS);
@@ -9284,16 +9282,6 @@
@SystemApi
public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
- /**
- * Whether the package manager should send package verification broadcasts for verifiers to
- * review apps prior to installation.
- * 1 = request apps to be verified prior to installation, if a verifier exists.
- * 0 = do not verify apps before installation
- * @hide
- */
- @UnsupportedAppUsage
- public static final String PACKAGE_VERIFIER_ENABLE = "package_verifier_enable";
-
/** Timeout for package verification.
* @hide */
public static final String PACKAGE_VERIFIER_TIMEOUT = "verifier_timeout";
@@ -10646,6 +10634,7 @@
/** {@hide} */
public static final String
BLUETOOTH_HEARING_AID_PRIORITY_PREFIX = "bluetooth_hearing_aid_priority_";
+
/**
* Enable/disable radio bug detection
*
@@ -11463,105 +11452,6 @@
"adb_allowed_connection_time";
/**
- * Get the key that retrieves a bluetooth headset's priority.
- * @hide
- */
- public static final String getBluetoothHeadsetPriorityKey(String address) {
- return BLUETOOTH_HEADSET_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth a2dp sink's priority.
- * @hide
- */
- public static final String getBluetoothA2dpSinkPriorityKey(String address) {
- return BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth a2dp src's priority.
- * @hide
- */
- public static final String getBluetoothA2dpSrcPriorityKey(String address) {
- return BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth a2dp device's ability to support optional codecs.
- * @hide
- */
- public static final String getBluetoothA2dpSupportsOptionalCodecsKey(String address) {
- return BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX +
- address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves whether a bluetooth a2dp device should have optional codecs
- * enabled.
- * @hide
- */
- public static final String getBluetoothA2dpOptionalCodecsEnabledKey(String address) {
- return BLUETOOTH_A2DP_OPTIONAL_CODECS_ENABLED_PREFIX +
- address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth Input Device's priority.
- * @hide
- */
- public static final String getBluetoothHidHostPriorityKey(String address) {
- return BLUETOOTH_INPUT_DEVICE_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth pan client priority.
- * @hide
- */
- public static final String getBluetoothPanPriorityKey(String address) {
- return BLUETOOTH_PAN_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth hearing aid priority.
- * @hide
- */
- public static final String getBluetoothHearingAidPriorityKey(String address) {
- return BLUETOOTH_HEARING_AID_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth map priority.
- * @hide
- */
- public static final String getBluetoothMapPriorityKey(String address) {
- return BLUETOOTH_MAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth map client priority.
- * @hide
- */
- public static final String getBluetoothMapClientPriorityKey(String address) {
- return BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth pbap client priority.
- * @hide
- */
- public static final String getBluetoothPbapClientPriorityKey(String address) {
- return BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
- * Get the key that retrieves a bluetooth sap priority.
- * @hide
- */
- public static final String getBluetoothSapPriorityKey(String address) {
- return BLUETOOTH_SAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
- }
-
- /**
* Scaling factor for normal window animations. Setting to 0 will
* disable window animations.
*/
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 7e09740..236e5ae 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,6 +41,7 @@
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
+ public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -48,6 +49,7 @@
DEFAULT_FLAGS = new HashMap<>();
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
+ DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "false");
DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6458737..1855a26 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,6 +161,8 @@
private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken,
int[] allowedConfigs);
private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken);
+ private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken,
+ int defaultModeId, float minRefreshRate, float maxRefreshRate);
private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries(
IBinder displayToken);
@@ -1492,6 +1494,19 @@
/**
* @hide
*/
+ public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken,
+ int defaultModeId, float minRefreshRate, float maxRefreshRate) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ return nativeSetDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+ maxRefreshRate);
+ }
+
+ /**
+ * @hide
+ */
public static int[] getDisplayColorModes(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 2af7ac7..4b47927 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -125,7 +125,7 @@
*
* @param webview the WebView to transport
*/
- public synchronized void setWebView(WebView webview) {
+ public synchronized void setWebView(@Nullable WebView webview) {
mWebview = webview;
}
@@ -134,6 +134,7 @@
*
* @return the transported WebView object
*/
+ @Nullable
public synchronized WebView getWebView() {
return mWebview;
}
@@ -309,7 +310,7 @@
*
* @param context an Activity Context to access application assets
*/
- public WebView(Context context) {
+ public WebView(@NonNull Context context) {
this(context, null);
}
@@ -319,7 +320,7 @@
* @param context an Activity Context to access application assets
* @param attrs an AttributeSet passed to our parent
*/
- public WebView(Context context, AttributeSet attrs) {
+ public WebView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.webViewStyle);
}
@@ -332,7 +333,7 @@
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
*/
- public WebView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@@ -349,7 +350,8 @@
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
*/
- public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
this(context, attrs, defStyleAttr, defStyleRes, null, false);
}
@@ -370,7 +372,7 @@
* and {@link WebStorage} for fine-grained control of privacy data.
*/
@Deprecated
- public WebView(Context context, AttributeSet attrs, int defStyleAttr,
+ public WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
boolean privateBrowsing) {
this(context, attrs, defStyleAttr, 0, null, privateBrowsing);
}
@@ -395,8 +397,8 @@
* be added synchronously, before a subsequent loadUrl call takes effect.
*/
@UnsupportedAppUsage
- protected WebView(Context context, AttributeSet attrs, int defStyleAttr,
- Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+ protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ @Nullable Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing);
}
@@ -405,8 +407,9 @@
*/
@SuppressWarnings("deprecation") // for super() call into deprecated base class constructor.
@UnsupportedAppUsage
- protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
- Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+ protected WebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes, @Nullable Map<String, Object> javaScriptInterfaces,
+ boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
// WebView is important by default, unless app developer overrode attribute.
@@ -642,7 +645,7 @@
* method fails.
*/
@Nullable
- public WebBackForwardList saveState(Bundle outState) {
+ public WebBackForwardList saveState(@NonNull Bundle outState) {
checkThread();
return mProvider.saveState(outState);
}
@@ -695,7 +698,7 @@
* @return the restored back/forward list or {@code null} if restoreState failed
*/
@Nullable
- public WebBackForwardList restoreState(Bundle inState) {
+ public WebBackForwardList restoreState(@NonNull Bundle inState) {
checkThread();
return mProvider.restoreState(inState);
}
@@ -713,7 +716,7 @@
* controlling caching, accept types or the User-Agent, their
* values may be overridden by this WebView's defaults.
*/
- public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+ public void loadUrl(@NonNull String url, @NonNull Map<String, String> additionalHttpHeaders) {
checkThread();
mProvider.loadUrl(url, additionalHttpHeaders);
}
@@ -725,7 +728,7 @@
*
* @param url the URL of the resource to load
*/
- public void loadUrl(String url) {
+ public void loadUrl(@NonNull String url) {
checkThread();
mProvider.loadUrl(url);
}
@@ -739,7 +742,7 @@
* @param postData the data will be passed to "POST" request, which must be
* be "application/x-www-form-urlencoded" encoded.
*/
- public void postUrl(String url, byte[] postData) {
+ public void postUrl(@NonNull String url, @NonNull byte[] postData) {
checkThread();
if (URLUtil.isNetworkUrl(url)) {
mProvider.postUrl(url, postData);
@@ -803,7 +806,8 @@
* @param mimeType the MIME type of the data, e.g. 'text/html'.
* @param encoding the encoding of the data
*/
- public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
+ public void loadData(@NonNull String data, @Nullable String mimeType,
+ @Nullable String encoding) {
checkThread();
mProvider.loadData(data, mimeType, encoding);
}
@@ -850,7 +854,7 @@
* @param historyUrl the URL to use as the history entry. If {@code null} defaults
* to 'about:blank'. If non-null, this must be a valid URL.
*/
- public void loadDataWithBaseURL(@Nullable String baseUrl, String data,
+ public void loadDataWithBaseURL(@Nullable String baseUrl, @NonNull String data,
@Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) {
checkThread();
mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
@@ -873,7 +877,8 @@
* completes with the result of the execution (if any).
* May be {@code null} if no notification of the result is required.
*/
- public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
+ public void evaluateJavascript(@NonNull String script, @Nullable ValueCallback<String>
+ resultCallback) {
checkThread();
mProvider.evaluateJavaScript(script, resultCallback);
}
@@ -883,7 +888,7 @@
*
* @param filename the filename where the archive should be placed
*/
- public void saveWebArchive(String filename) {
+ public void saveWebArchive(@NonNull String filename) {
checkThread();
mProvider.saveWebArchive(filename);
}
@@ -900,8 +905,8 @@
* under which the file was saved, or {@code null} if saving the
* file failed.
*/
- public void saveWebArchive(String basename, boolean autoname, @Nullable ValueCallback<String>
- callback) {
+ public void saveWebArchive(@NonNull String basename, boolean autoname,
+ @Nullable ValueCallback<String> callback) {
checkThread();
mProvider.saveWebArchive(basename, autoname, callback);
}
@@ -1064,7 +1069,7 @@
* requests with callbacks.
* @param callback The callback to be invoked.
*/
- public void postVisualStateCallback(long requestId, VisualStateCallback callback) {
+ public void postVisualStateCallback(long requestId, @NonNull VisualStateCallback callback) {
checkThread();
mProvider.insertVisualStateCallback(requestId, callback);
}
@@ -1131,7 +1136,8 @@
* @param documentName The user-facing name of the printed document. See
* {@link android.print.PrintDocumentInfo}
*/
- public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
+ @NonNull
+ public PrintDocumentAdapter createPrintDocumentAdapter(@NonNull String documentName) {
checkThread();
return mProvider.createPrintDocumentAdapter(documentName);
}
@@ -1203,6 +1209,7 @@
* and the email is set in the "extra" field of HitTestResult. Otherwise,
* HitTestResult type is set to UNKNOWN_TYPE.
*/
+ @NonNull
public HitTestResult getHitTestResult() {
checkThread();
return mProvider.getHitTestResult();
@@ -1233,7 +1240,7 @@
* @param msg the message to be dispatched with the result of the request
* as the data member with "url" as key. The result can be {@code null}.
*/
- public void requestImageRef(Message msg) {
+ public void requestImageRef(@NonNull Message msg) {
checkThread();
mProvider.requestImageRef(msg);
}
@@ -1243,10 +1250,11 @@
* passed to WebViewClient.onPageStarted because although the load for
* that URL has begun, the current page may not have changed.
*
- * @return the URL for the current page
+ * @return the URL for the current page or {@code null} if no page has been loaded
*/
@InspectableProperty(hasAttributeId = false)
@ViewDebug.ExportedProperty(category = "webview")
+ @Nullable
public String getUrl() {
checkThread();
return mProvider.getUrl();
@@ -1259,10 +1267,12 @@
* Also, there may have been redirects resulting in a different URL to that
* originally requested.
*
- * @return the URL that was originally requested for the current page
+ * @return the URL that was originally requested for the current page or
+ * {@code null} if no page has been loaded
*/
@InspectableProperty(hasAttributeId = false)
@ViewDebug.ExportedProperty(category = "webview")
+ @Nullable
public String getOriginalUrl() {
checkThread();
return mProvider.getOriginalUrl();
@@ -1272,10 +1282,11 @@
* Gets the title for the current page. This is the title of the current page
* until WebViewClient.onReceivedTitle is called.
*
- * @return the title for the current page
+ * @return the title for the current page or {@code null} if no page has been loaded
*/
@InspectableProperty(hasAttributeId = false)
@ViewDebug.ExportedProperty(category = "webview")
+ @Nullable
public String getTitle() {
checkThread();
return mProvider.getTitle();
@@ -1285,9 +1296,11 @@
* Gets the favicon for the current page. This is the favicon of the current
* page until WebViewClient.onReceivedIcon is called.
*
- * @return the favicon for the current page
+ * @return the favicon for the current page or {@code null} if the page doesn't
+ * have one or if no page has been loaded
*/
@InspectableProperty(hasAttributeId = false)
+ @Nullable
public Bitmap getFavicon() {
checkThread();
return mProvider.getFavicon();
@@ -1523,6 +1536,7 @@
* different objects. The object returned from this method will not be
* updated to reflect any new state.
*/
+ @NonNull
public WebBackForwardList copyBackForwardList() {
checkThread();
return mProvider.copyBackForwardList();
@@ -1535,7 +1549,7 @@
*
* @param listener an implementation of {@link FindListener}
*/
- public void setFindListener(FindListener listener) {
+ public void setFindListener(@Nullable FindListener listener) {
checkThread();
setupFindListenerIfNeeded();
mFindListener.mUserFindListener = listener;
@@ -1580,7 +1594,7 @@
* @param find the string to find.
* @see #setFindListener
*/
- public void findAllAsync(String find) {
+ public void findAllAsync(@NonNull String find) {
checkThread();
mProvider.findAllAsync(find);
}
@@ -1682,7 +1696,7 @@
*
* @param response the message that will be dispatched with the result
*/
- public void documentHasImages(Message response) {
+ public void documentHasImages(@NonNull Message response) {
checkThread();
mProvider.documentHasImages(response);
}
@@ -1694,7 +1708,7 @@
* @param client an implementation of WebViewClient
* @see #getWebViewClient
*/
- public void setWebViewClient(WebViewClient client) {
+ public void setWebViewClient(@NonNull WebViewClient client) {
checkThread();
mProvider.setWebViewClient(client);
}
@@ -1705,6 +1719,7 @@
* @return the WebViewClient, or a default client if not yet set
* @see #setWebViewClient
*/
+ @NonNull
public WebViewClient getWebViewClient() {
checkThread();
return mProvider.getWebViewClient();
@@ -1798,7 +1813,7 @@
*
* @param listener an implementation of DownloadListener
*/
- public void setDownloadListener(DownloadListener listener) {
+ public void setDownloadListener(@Nullable DownloadListener listener) {
checkThread();
mProvider.setDownloadListener(listener);
}
@@ -1811,7 +1826,7 @@
* @param client an implementation of WebChromeClient
* @see #getWebChromeClient
*/
- public void setWebChromeClient(WebChromeClient client) {
+ public void setWebChromeClient(@Nullable WebChromeClient client) {
checkThread();
mProvider.setWebChromeClient(client);
}
@@ -1898,7 +1913,7 @@
* context. {@code null} values are ignored.
* @param name the name used to expose the object in JavaScript
*/
- public void addJavascriptInterface(Object object, String name) {
+ public void addJavascriptInterface(@NonNull Object object, @NonNull String name) {
checkThread();
mProvider.addJavascriptInterface(object, name);
}
@@ -1926,6 +1941,7 @@
*
* @return the two message ports that form the message channel.
*/
+ @NonNull
public WebMessagePort[] createWebMessageChannel() {
checkThread();
return mProvider.createWebMessageChannel();
@@ -1950,7 +1966,7 @@
* @param message the WebMessage
* @param targetOrigin the target origin.
*/
- public void postWebMessage(WebMessage message, Uri targetOrigin) {
+ public void postWebMessage(@NonNull WebMessage message, @NonNull Uri targetOrigin) {
checkThread();
mProvider.postMessageToMainFrame(message, targetOrigin);
}
@@ -1962,6 +1978,7 @@
* @return a WebSettings object that can be used to control this WebView's
* settings
*/
+ @NonNull
public WebSettings getSettings() {
checkThread();
return mProvider.getSettings();
@@ -2026,7 +2043,7 @@
* in the current process.
* @throws IllegalArgumentException if the suffix contains a path separator.
*/
- public static void setDataDirectorySuffix(String suffix) {
+ public static void setDataDirectorySuffix(@NonNull String suffix) {
WebViewFactory.setDataDirectorySuffix(suffix);
}
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index fee8345..0847fbd 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -33,7 +33,6 @@
import android.content.pm.PackageParser.PackageParserException;
import android.os.Build;
import android.os.SELinux;
-import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;
@@ -444,6 +443,24 @@
return sum;
}
+ /**
+ * Configure the native library files managed by Incremental Service. Makes sure Incremental
+ * Service will create native library directories and set up native library binary files in the
+ * same structure as they are in non-incremental installations.
+ *
+ * @param pkg The package to be installed, including all the APK files.
+ * @param handle The pointer to an zip archive.
+ * @param libraryRoot The root directory of the native library files, e.g., lib/
+ * @param abiList The list of ABIs that are supported by the current device.
+ * @param useIsaSubdir Whether or not to set up a sub dir for the ISA.
+ * @return ABI code if installation succeeds or error code if installation fails.
+ */
+ public static int configureNativeBinariesForSupportedAbi(Package pkg, Handle handle,
+ File libraryRoot, String[] abiList, boolean useIsaSubdir) {
+ // TODO(b/136132412): Implement this.
+ return -1;
+ }
+
// We don't care about the other return values for now.
private static final int BITCODE_PRESENT = 1;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 75a5804..b350028 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1870,6 +1870,7 @@
mCount = computeCurrentCountLocked();
mUnpluggedReportedTotalTime = mCurrentReportedTotalTime = 0;
mUnpluggedReportedCount = mCurrentReportedCount = 0;
+ mTrackingReportedValues = false;
}
public void setUpdateVersion(int version) {
@@ -10800,10 +10801,10 @@
mHasWifiReporting = true;
// Measured in mAms
- final long txTimeMs = info.getControllerTxTimeMillis();
- final long rxTimeMs = info.getControllerRxTimeMillis();
- final long scanTimeMs = info.getControllerScanTimeMillis();
- final long idleTimeMs = info.getControllerIdleTimeMillis();
+ final long txTimeMs = info.getControllerTxDurationMillis();
+ final long rxTimeMs = info.getControllerRxDurationMillis();
+ final long scanTimeMs = info.getControllerScanDurationMillis();
+ final long idleTimeMs = info.getControllerIdleDurationMillis();
final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
long leftOverRxTimeMs = rxTimeMs;
@@ -10946,13 +10947,14 @@
// Update WiFi controller stats.
- mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
+ mWifiActivity.getRxTimeCounter().addCountLocked(
+ info.getControllerRxDurationMillis());
mWifiActivity.getTxTimeCounters()[0].addCountLocked(
- info.getControllerTxTimeMillis());
+ info.getControllerTxDurationMillis());
mWifiActivity.getScanTimeCounter().addCountLocked(
- info.getControllerScanTimeMillis());
+ info.getControllerScanDurationMillis());
mWifiActivity.getIdleTimeCounter().addCountLocked(
- info.getControllerIdleTimeMillis());
+ info.getControllerIdleDurationMillis());
// POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
final double opVolt = mPowerProfile.getAveragePower(
@@ -10960,7 +10962,7 @@
if (opVolt != 0) {
// We store the power drain as mAms.
mWifiActivity.getPowerCounter().addCountLocked(
- (long) (info.getControllerEnergyUsed() / opVolt));
+ (long) (info.getControllerEnergyUsedMicroJoules() / opVolt));
}
// Converting uWs to mAms.
// Conversion: (uWs * (1000ms / 1s) * (1mW / 1000uW)) / mV = mAms
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 51f8fcc..148b0a2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,6 +110,7 @@
"android_view_InputEventReceiver.cpp",
"android_view_InputEventSender.cpp",
"android_view_InputQueue.cpp",
+ "android_view_FrameMetricsObserver.cpp",
"android_view_KeyCharacterMap.cpp",
"android_view_KeyEvent.cpp",
"android_view_MotionEvent.cpp",
@@ -424,9 +425,9 @@
android: {
srcs: [ // sources that depend on android only libraries
"android/graphics/apex/android_canvas.cpp",
+ "android/graphics/apex/renderthread.cpp",
"android/graphics/apex/jni_runtime.cpp",
- "android_view_FrameMetricsObserver.cpp",
"android_view_TextureLayer.cpp",
"android_view_ThreadedRenderer.cpp",
"android/graphics/BitmapRegionDecoder.cpp",
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 2dec4b3..984f93c 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -12,7 +12,6 @@
#include "SkWebpEncoder.h"
#include "android_os_Parcel.h"
-#include "android_util_Binder.h"
#include "android_nio_utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include <hwui/Paint.h>
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 6ffa72a..f18632d 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -25,18 +25,13 @@
#include "SkBitmapRegionDecoder.h"
#include "SkCodec.h"
#include "SkData.h"
-#include "SkUtils.h"
-#include "SkPixelRef.h"
#include "SkStream.h"
-#include "android_nio_utils.h"
-#include "android_util_Binder.h"
#include "core_jni_helpers.h"
#include <HardwareBitmapUploader.h>
#include <nativehelper/JNIHelp.h>
#include <androidfw/Asset.h>
-#include <binder/Parcel.h>
#include <jni.h>
#include <sys/stat.h>
diff --git a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
index 7a9fea7..4de6c86 100644
--- a/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
+++ b/core/jni/android/graphics/CreateJavaOutputStreamAdaptor.cpp
@@ -7,6 +7,7 @@
#include "Utils.h"
#include <nativehelper/JNIHelp.h>
+#include <log/log.h>
#include <memory>
static jmethodID gInputStream_readMethodID;
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index bd28fe0..6095ffa 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -5,6 +5,7 @@
#include "SkShader.h"
#include "SkBlendMode.h"
#include "core_jni_helpers.h"
+#include "src/shaders/SkRTShader.h"
#include <jni.h>
@@ -212,6 +213,44 @@
///////////////////////////////////////////////////////////////////////////////////////////////
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
+ jbyteArray inputs, jlong colorSpaceHandle) {
+ SkRuntimeShaderFactory* factory = reinterpret_cast<SkRuntimeShaderFactory*>(shaderFactory);
+ AutoJavaByteArray arInputs(env, inputs);
+
+ sk_sp<SkData> fData;
+ fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ sk_sp<SkShader> shader = factory->make(fData, matrix);
+ ThrowIAE_IfNull(env, shader);
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl,
+ jboolean isOpaque) {
+ ScopedUtfChars strSksl(env, sksl);
+ SkRuntimeShaderFactory* shaderFactory = new SkRuntimeShaderFactory(SkString(strSksl.c_str()),
+ isOpaque == JNI_TRUE);
+ ThrowIAE_IfNull(env, shaderFactory);
+
+ return reinterpret_cast<jlong>(shaderFactory);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void RuntimeShader_delete(SkRuntimeShaderFactory* shaderFactory) {
+ delete shaderFactory;
+}
+
+static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RuntimeShader_delete));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
static const JNINativeMethod gColorMethods[] = {
{ "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
{ "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
@@ -241,6 +280,13 @@
{ "nativeCreate", "(JJJI)J", (void*)ComposeShader_create },
};
+static const JNINativeMethod gRuntimeShaderMethods[] = {
+ { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer },
+ { "nativeCreate", "(JJ[BJ)J", (void*)RuntimeShader_create },
+ { "nativeCreateShaderFactory", "(Ljava/lang/String;Z)J",
+ (void*)RuntimeShader_createShaderFactory },
+};
+
int register_android_graphics_Shader(JNIEnv* env)
{
android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
@@ -257,6 +303,8 @@
NELEM(gSweepGradientMethods));
android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
NELEM(gComposeShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
+ NELEM(gRuntimeShaderMethods));
return 0;
}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 9603a10..4ce56ba 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -22,8 +22,6 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include "SkTypeface.h"
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager.h>
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
#include <minikin/SystemFonts.h>
diff --git a/core/jni/android/graphics/Utils.cpp b/core/jni/android/graphics/Utils.cpp
index 462d052..17c194d 100644
--- a/core/jni/android/graphics/Utils.cpp
+++ b/core/jni/android/graphics/Utils.cpp
@@ -18,6 +18,8 @@
#include "SkUtils.h"
#include "SkData.h"
+#include <log/log.h>
+
using namespace android;
AssetStreamAdaptor::AssetStreamAdaptor(Asset* asset)
diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h
index ac291ea..8925517 100644
--- a/core/jni/android/graphics/Utils.h
+++ b/core/jni/android/graphics/Utils.h
@@ -19,8 +19,6 @@
#include "SkStream.h"
-#include "android_util_Binder.h"
-
#include <jni.h>
#include <androidfw/Asset.h>
diff --git a/core/jni/android/graphics/apex/include/android/graphics/renderthread.h b/core/jni/android/graphics/apex/include/android/graphics/renderthread.h
new file mode 100644
index 0000000..0a790af
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/renderthread.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+#ifndef ANDROID_GRAPHICS_RENDERTHREAD_H
+#define ANDROID_GRAPHICS_RENDERTHREAD_H
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Dumps a textual representation of the graphics stats for this process.
+ * @param fd The file descriptor that the available graphics stats will be appended to. The
+ * function requires a valid fd, but does not persist or assume ownership of the fd
+ * outside the scope of this function.
+ */
+void ARenderThread_dumpGraphicsMemory(int fd);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_RENDERTHREAD_H
diff --git a/core/jni/android/graphics/apex/renderthread.cpp b/core/jni/android/graphics/apex/renderthread.cpp
new file mode 100644
index 0000000..5d26afe
--- /dev/null
+++ b/core/jni/android/graphics/apex/renderthread.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#include "android/graphics/renderthread.h"
+
+#include <renderthread/RenderProxy.h>
+
+using namespace android;
+
+void ARenderThread_dumpGraphicsMemory(int fd) {
+ uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+}
diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp
index ca8b8de..5aa684d 100644
--- a/core/jni/android_app_ActivityThread.cpp
+++ b/core/jni/android_app_ActivityThread.cpp
@@ -17,13 +17,11 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
-#include <minikin/Layout.h>
-#include <renderthread/RenderProxy.h>
-
#include "core_jni_helpers.h"
#include <unistd.h>
#include <bionic/malloc.h>
+#include <android/graphics/renderthread.h>
namespace android {
@@ -35,7 +33,7 @@
static void
android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
- android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);
+ ARenderThread_dumpGraphicsMemory(fd);
}
static void android_app_ActivityThread_initZygoteChildHeapProfiling(JNIEnv* env, jobject clazz) {
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index cb7f0dd..f2a51ad 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -396,9 +396,8 @@
}
static sp<ANativeWindow> getSurfaceTextureNativeWindow(JNIEnv* env, jobject thiz) {
- sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
- sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
+ sp<Surface> surfaceTextureClient(producer != NULL ? new Surface(producer) : NULL);
return surfaceTextureClient;
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 2232393..0992beb 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -30,12 +30,13 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
-#include <binder/IInterface.h>
-#include <binder/IServiceManager.h>
-#include <binder/IPCThreadState.h>
-#include <binder/Parcel.h>
#include <binder/BpBinder.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
#include <binder/ProcessState.h>
+#include <binder/Stability.h>
#include <cutils/atomic.h>
#include <log/log.h>
#include <utils/KeyedVector.h>
@@ -459,6 +460,9 @@
sp<JavaBBinder> b = mBinder.promote();
if (b == NULL) {
b = new JavaBBinder(env, obj);
+ if (mVintf) {
+ ::android::internal::Stability::markVintf(b.get());
+ }
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
b.get(), b->getWeakRefs(), obj, b->getWeakRefs()->getWeakCount());
@@ -473,9 +477,18 @@
return mBinder.promote();
}
+ void markVintf() {
+ mVintf = true;
+ }
+
private:
Mutex mLock;
wp<JavaBBinder> mBinder;
+
+ // in the future, we might condense this into int32_t stability, or if there
+ // is too much binder state here, we can think about making JavaBBinder an
+ // sp here (avoid recreating it)
+ bool mVintf = false;
};
// ----------------------------------------------------------------------------
@@ -965,6 +978,12 @@
IPCThreadState::self()->restoreCallingWorkSource(token);
}
+static void android_os_Binder_markVintfStability(JNIEnv* env, jobject clazz) {
+ JavaBBinderHolder* jbh =
+ (JavaBBinderHolder*) env->GetLongField(clazz, gBinderOffsets.mObject);
+ jbh->markVintf();
+}
+
static void android_os_Binder_flushPendingCommands(JNIEnv* env, jobject clazz)
{
IPCThreadState::self()->flushCommands();
@@ -1041,6 +1060,7 @@
// @CriticalNative
{ "clearCallingWorkSource", "()J", (void*)android_os_Binder_clearCallingWorkSource },
{ "restoreCallingWorkSource", "(J)V", (void*)android_os_Binder_restoreCallingWorkSource },
+ { "markVintfStability", "()V", (void*)android_os_Binder_markVintfStability},
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9f20388..bd202c1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -810,6 +810,16 @@
return allowedConfigsArray;
}
+static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz,
+ jobject tokenObj, jint displayModeId, jfloat minRefreshRate, jfloat maxRefreshRate) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+ if (token == nullptr) return JNI_FALSE;
+
+ size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs(
+ token, displayModeId, minRefreshRate, maxRefreshRate);
+ return result == NO_ERROR ? JNI_TRUE : JNI_FALSE;
+}
+
static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
if (token == NULL) return -1;
@@ -1366,6 +1376,8 @@
(void*)nativeSetAllowedDisplayConfigs },
{"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I",
(void*)nativeGetAllowedDisplayConfigs },
+ {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;IFF)Z",
+ (void*)nativeSetDesiredDisplayConfigSpecs },
{"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I",
(void*)nativeGetDisplayColorModes},
{"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;",
diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp
index 6475151..5491a33 100644
--- a/core/jni/android_view_TextureLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "OpenGLRenderer"
#include "jni.h"
-#include "GraphicsJNI.h"
#include <nativehelper/JNIHelp.h>
#include "core_jni_helpers.h"
@@ -27,12 +26,8 @@
#include <gui/surfacetexture/surface_texture_platform.h>
#include <gui/surfacetexture/SurfaceTexture.h>
#include <hwui/Paint.h>
-
#include <SkMatrix.h>
-
#include <DeferredLayerUpdater.h>
-#include <Rect.h>
-#include <RenderNode.h>
namespace android {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 8fabb23..fd6984b 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -36,6 +36,7 @@
"/apex/com.android.conscrypt/javalib/conscrypt.jar",
"/apex/com.android.ipsec/javalib/ike.jar",
"/apex/com.android.media/javalib/updatable-media.jar",
+ "/apex/com.android.sdkext/javalib/framework-sdkext.jar",
"/dev/null",
"/dev/socket/zygote",
"/dev/socket/zygote_secondary",
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index d181436..ab97fdd 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -693,6 +693,12 @@
// CATEGORY: SETTINGS
// OS: R
ACTION_CONTROLLER_UPDATE_STATE = 1728;
+
+ // Custom tag to evaluate the consuming time from onAttach to
+ // DashboardFragment.updatePreferenceStates.
+ // CATEGORY: SETTINGS
+ // OS: R
+ ACTION_DASHBOARD_VISIBLE_TIME = 1729;
}
/**
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 99dfffe..b83294c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -635,6 +635,11 @@
<protected-broadcast android:name="android.intent.action.DEVICE_CUSTOMIZATION_READY" />
+ <!-- NETWORK_SET_TIME / NETWORK_SET_TIMEZONE moved from com.android.phone to system server.
+ They should ultimately be removed. -->
+ <protected-broadcast android:name="android.intent.action.NETWORK_SET_TIME" />
+ <protected-broadcast android:name="android.intent.action.NETWORK_SET_TIMEZONE" />
+
<!-- For tether entitlement recheck-->
<protected-broadcast
android:name="com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM" />
@@ -1668,13 +1673,13 @@
<permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"
android:protectionLevel="signature" />
- <!-- #SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can be increased
- when the device is stationary in order to save power.
+ <!-- @SystemApi @hide Allows device mobility state to be set so that Wifi scan interval can
+ be increased when the device is stationary in order to save power.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WIFI_SET_DEVICE_MOBILITY_STATE"
android:protectionLevel="signature|privileged" />
- <!-- #SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
+ <!-- @SystemApi @hide Allows privileged system APK to update Wifi usability stats and score.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index d095690..4acdeeb 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Dien in"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bestuurprogram werk tans"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tik om die bestuurprogram te verlaat."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Verbinding of Wi-Fi-warmkol aktief"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tik om op te stel."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Verbinding is gedeaktiveer"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontak jou administrateur vir besonderhede"</string>
<string name="back_button_label" msgid="4078224038025043387">"Terug"</string>
<string name="next_button_label" msgid="6040209156399907780">"Volgende"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Slaan oor"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 75444ea..c6e55c3 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"አስረክብ"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"የመንዳት መተግበሪያ እያሄደ ነው"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ከመንዳት መተግበሪያ ለመውጣት መታ ያድርጉ።"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ለማዋቀር መታ ያድርጉ።"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
<string name="back_button_label" msgid="4078224038025043387">"ተመለስ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ቀጥሎ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ዝለል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 9928436..609b77e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1509,10 +1509,6 @@
<string name="submit" msgid="862795280643405865">"إرسال"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"تطبيق القيادة قيد التشغيل"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"انقر للخروج من تطبيق القيادة."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"النطاق أو نقطة الاتصال نشطة"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"انقر للإعداد."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"تم إيقاف التوصيل"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"اتصل بالمشرف للحصول على التفاصيل"</string>
<string name="back_button_label" msgid="4078224038025043387">"رجوع"</string>
<string name="next_button_label" msgid="6040209156399907780">"التالي"</string>
<string name="skip_button_label" msgid="3566599811326688389">"التخطي"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index cfd6239..091b563 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"দাখিল কৰক"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ড্ৰাইভিং এপ্ চলি আছে"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ড্ৰাইভিং এপৰ পৰা বাহিৰ হ\'বলৈ টিপক।"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ছেট আপ কৰিবলৈ টিপক।"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
<string name="back_button_label" msgid="4078224038025043387">"উভতি যাওক"</string>
<string name="next_button_label" msgid="6040209156399907780">"পৰৱৰ্তী"</string>
<string name="skip_button_label" msgid="3566599811326688389">"এৰি যাওক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index a26d49d..359dfb7 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Göndər"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sürücülük tətbiqi işləyir"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sürücülük tətbiqindən çıxmaq üçün klikləyin."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tezerinq və ya hotspot aktivdir"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Quraşdırmaq üçün tıklayın."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Birləşmə deaktivdir"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Məlumat üçün adminlə əlaqə saxlayın"</string>
<string name="back_button_label" msgid="4078224038025043387">"Geri"</string>
<string name="next_button_label" msgid="6040209156399907780">"Növbəti"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Keç"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 6d0ae6b..a27d359 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1449,10 +1449,6 @@
<string name="submit" msgid="862795280643405865">"Pošalji"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnju je pokrenuta"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite da biste izašli iz aplikacije za vožnju."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite da biste podesili."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Privezivanje je onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Potražite detalje od administratora"</string>
<string name="back_button_label" msgid="4078224038025043387">"Nazad"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 390e8b1..fb0fa33 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Перадаць"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Праграма для ваджэння ўключана"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Націсніце, каб выйсці з праграмы для ваджэння."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Дакраніцеся, каб наладзіць."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Рэжым мадэма адключаны"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Далей"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Прапусціць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index d47229df..69c998d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Изпращане"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Приложението за шофиране е включено"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Докоснете, за да излезете от приложението за шофиране."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Има активна споделена връзка или безжична точка за достъп"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Докоснете, за да настроите."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Функцията за тетъринг е деактивирана"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Свържете се с администратора си за подробности"</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Напред"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Пропускане"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index a767a71..e335396 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"জমা দিন"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ড্রাইভিং অ্যাপ চালু আছে"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ড্রাইভিং অ্যাপ বন্ধ করতে ট্যাপ করুন।"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"টিথারিং বা হটস্পট সক্রিয় আছে"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"সেট-আপ করার জন্য আলতো চাপুন৷"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"টিথারিং অক্ষম করা আছে"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string>
<string name="back_button_label" msgid="4078224038025043387">"ফিরুন"</string>
<string name="next_button_label" msgid="6040209156399907780">"পরবর্তী"</string>
<string name="skip_button_label" msgid="3566599811326688389">"এড়িয়ে যান"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f1c1500..d7bd7a1 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1451,10 +1451,6 @@
<string name="submit" msgid="862795280643405865">"Potvrdi"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnju je pokrenuta"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite za izlaz iz aplikacije za vožnju."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite za postavke"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Povezivanje putem mobitela je onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontaktirajte svog administratora za dodatne detalje"</string>
<string name="back_button_label" msgid="4078224038025043387">"Nazad"</string>
<string name="next_button_label" msgid="6040209156399907780">"Naprijed"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4b19405..1f7d82e 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Envia"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"S\'està executant l\'aplicació de conducció"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca per sortir de l\'aplicació de conducció."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Toca per configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"La compartició de xarxa està desactivada"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacta amb el teu administrador per obtenir més informació"</string>
<string name="back_button_label" msgid="4078224038025043387">"Enrere"</string>
<string name="next_button_label" msgid="6040209156399907780">"Següent"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Omet"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 22fd4c6..e05f299 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Odeslat"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Jízdní aplikace je spuštěna"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Jízdní aplikaci zavřete klepnutím."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Sdílené připojení nebo hotspot je aktivní."</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Klepnutím zahájíte nastavení."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering je zakázán"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"O podrobnosti požádejte administrátora"</string>
<string name="back_button_label" msgid="4078224038025043387">"Zpět"</string>
<string name="next_button_label" msgid="6040209156399907780">"Další"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Přeskočit"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 4475a1b..772b7b0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Send"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bilkørselsappen er aktiv"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tryk for at lukke bilkørselsappen."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Netdeling eller hotspot er aktivt"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tryk for at konfigurere"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Netdeling er deaktiveret"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakt din administrator for at få oplysninger"</string>
<string name="back_button_label" msgid="4078224038025043387">"Tilbage"</string>
<string name="next_button_label" msgid="6040209156399907780">"Næste"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Spring over"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 2489fca..bb29a0e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Senden"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Fahr-App wird ausgeführt"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tippen, um die Fahr-App zu beenden."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering oder Hotspot aktiv"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Zum Einrichten tippen."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering ist deaktiviert"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Bitte wende dich für weitere Informationen an den Administrator"</string>
<string name="back_button_label" msgid="4078224038025043387">"Zurück"</string>
<string name="next_button_label" msgid="6040209156399907780">"Weiter"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Überspringen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 6c76a1f..807c4c2 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Υποβολή"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Η εφαρμογή οδήγησης εκτελείται"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Πατήστε για να εξέλθετε από την εφαρμογή οδήγησης."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Πατήστε για ρύθμιση."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Η σύνδεση είναι απενεργοποιημένη"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
<string name="back_button_label" msgid="4078224038025043387">"Πίσω"</string>
<string name="next_button_label" msgid="6040209156399907780">"Επόμενο"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Παράλειψη"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Submit"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
<string name="back_button_label" msgid="4078224038025043387">"Back"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2cff16f..e538587 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Submit"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
<string name="back_button_label" msgid="4078224038025043387">"Back"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Submit"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
<string name="back_button_label" msgid="4078224038025043387">"Back"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e5f610b..8d2f805 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Submit"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
<string name="back_button_label" msgid="4078224038025043387">"Back"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6d536e4..1436692 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Submit"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Driving app is running"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tap to exit driving app."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering or hotspot active"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tap to set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is disabled"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contact your admin for details"</string>
<string name="back_button_label" msgid="4078224038025043387">"Back"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Skip"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3b9c7ad..b04882a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Se está ejecutando la app de conducción"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Presiona para salir de la app de conducción."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Anclaje a red o zona activa conectados"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Presiona para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Para obtener más información, comunícate con el administrador"</string>
<string name="back_button_label" msgid="4078224038025043387">"Atrás"</string>
<string name="next_button_label" msgid="6040209156399907780">"Siguiente"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2e74904..81f210b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicación de conducción en uso"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca para salir de la aplicación de conducción."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Compartir conexión/Zona Wi-Fi activada"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Toca para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"La conexión compartida está inhabilitada"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Ponte en contacto con el administrador para obtener más información"</string>
<string name="back_button_label" msgid="4078224038025043387">"Atrás"</string>
<string name="next_button_label" msgid="6040209156399907780">"Siguiente"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Saltar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 8561254..61b18bb 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Saada"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sõidurakendus töötab"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Puudutage sõidurakendusest väljumiseks."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Jagamine või kuumkoht on aktiivne"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Puudutage seadistamiseks."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Jagamine on keelatud"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
<string name="back_button_label" msgid="4078224038025043387">"Tagasi"</string>
<string name="next_button_label" msgid="6040209156399907780">"Järgmine"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Jäta vahele"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 263b5ed..fda8565 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Bidali"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Abian da gidatzeko aplikazioa"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sakatu gidatzeko aplikaziotik irteteko."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Konexioa partekatzea edo sare publikoa aktibo"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Sakatu konfiguratzeko."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Desgaituta dago konexioa partekatzeko aukera"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
<string name="back_button_label" msgid="4078224038025043387">"Atzera"</string>
<string name="next_button_label" msgid="6040209156399907780">"Hurrengoa"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Saltatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 997b6af..433d4f3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ارسال"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"برنامه رانندگی درحال اجرا است"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"برای خروج از برنامه رانندگی ضربه بزنید."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"اشتراکگذاری اینترنت یا نقطه اتصال فعال"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"برای راهاندازی ضربه بزنید."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"اشتراکگذاری اینترنت غیرفعال است"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
<string name="back_button_label" msgid="4078224038025043387">"برگشت"</string>
<string name="next_button_label" msgid="6040209156399907780">"بعدی"</string>
<string name="skip_button_label" msgid="3566599811326688389">"رد شدن"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 86badf6..0c7bd77 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Lähetä"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Ajosovellus on käynnissä"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Poistu ajosovelluksesta napauttamalla."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Internetin jakaminen tai yhteyspiste käytössä"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Määritä napauttamalla."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Yhteyden jakaminen poistettu käytöstä"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kysy lisätietoja järjestelmänvalvojalta."</string>
<string name="back_button_label" msgid="4078224038025043387">"Takaisin"</string>
<string name="next_button_label" msgid="6040209156399907780">"Seuraava"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Ohita"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2b89065..3c8f75f 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Envoyer"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"L\'application de conduite est en cours d\'exécution"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Touchez pour quitter l\'application de conduite."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Partage de connexion ou point d\'accès sans fil activé"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Touchez pour configurer."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Le partage de connexion est désactivé"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
<string name="back_button_label" msgid="4078224038025043387">"Précédent"</string>
<string name="next_button_label" msgid="6040209156399907780">"Suivante"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Passer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 7522b5aa..1b53bd9 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Envoyer"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"L\'application de conduite est en cours d\'exécution"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Appuyez ici pour quitter l\'application de conduite."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Partage de connexion ou point d\'accès sans fil activé"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Appuyez ici pour configurer."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Le partage de connexion est désactivé"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Pour en savoir plus, contactez votre administrateur"</string>
<string name="back_button_label" msgid="4078224038025043387">"Retour"</string>
<string name="next_button_label" msgid="6040209156399907780">"Suivant"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Ignorer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 4d40e21..4572db7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Estase executando a aplicación de condución"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toca para saír da aplicación de condución."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Conexión compartida ou zona wifi activada"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tocar para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"A conexión compartida está desactivada"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacta co administrador para obter información"</string>
<string name="back_button_label" msgid="4078224038025043387">"Volver"</string>
<string name="next_button_label" msgid="6040209156399907780">"Seguinte"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Omitir"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index ea3a573..5e4f753 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"સબમિટ કરો"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ડ્રાઇવિંગ ઍપ ચાલી રહી છે"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ડ્રાઇવિંગ ઍપથી બહાર નીકળવા માટે ટૅપ કરો."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"સેટ કરવા માટે ટૅપ કરો."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ટિથરિંગ અક્ષમ કરેલ છે"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
<string name="back_button_label" msgid="4078224038025043387">"પાછળ"</string>
<string name="next_button_label" msgid="6040209156399907780">"આગલું"</string>
<string name="skip_button_label" msgid="3566599811326688389">"છોડો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e12fa08..eecfd5b 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"सबमिट करें"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्राइविंग ऐप्लिकेशन चल रहा है"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्राइविंग ऐप्लिकेशन से निकलने के लिए टैप करें."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"टेदरिंग या हॉटस्पॉट सक्रिय"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"सेट करने के लिए टैप करें."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिंग अक्षम है"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
<string name="back_button_label" msgid="4078224038025043387">"वापस जाएं"</string>
<string name="next_button_label" msgid="6040209156399907780">"आगे बढ़ें"</string>
<string name="skip_button_label" msgid="3566599811326688389">"अभी नहीं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0871d06..e8f02b2 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1449,10 +1449,6 @@
<string name="submit" msgid="862795280643405865">"Pošalji"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Pokrenuta je aplikacija za vožnju"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dodirnite za zatvaranje aplikacije za vožnju."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Ograničenje ili aktivan hotspot"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Dodirnite da biste postavili."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Modemsko je povezivanje onemogućeno"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Obratite se administratoru da biste saznali pojedinosti"</string>
<string name="back_button_label" msgid="4078224038025043387">"Natrag"</string>
<string name="next_button_label" msgid="6040209156399907780">"Dalje"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index ce11fc6..1d1b994 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Elküldés"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Az autós alkalmazás fut"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Koppintson ide az autós alkalmazásból való kilépéshez."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Megosztás vagy aktív hotspot"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Koppintson a beállításhoz."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Az internetmegosztás le van tiltva"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"A részletekért forduljon rendszergazdájához"</string>
<string name="back_button_label" msgid="4078224038025043387">"Vissza"</string>
<string name="next_button_label" msgid="6040209156399907780">"Tovább"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Kihagyás"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 629b225..599ce86 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Ուղարկել"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Հավելվածն աշխատում է"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Հպեք՝ հավելվածը փակելու համար:"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Մոդեմի ռեժիմը միացված է"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Հպեք՝ կարգավորելու համար:"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Մոդեմի ռեժիմն անջատված է"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
<string name="back_button_label" msgid="4078224038025043387">"Հետ"</string>
<string name="next_button_label" msgid="6040209156399907780">"Հաջորդը"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Բաց թողնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f99c107..1f81eb5 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Kirim"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikasi mengemudi sedang berjalan"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketuk untuk keluar dari aplikasi mengemudi."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering (Penambatan) atau hotspot aktif"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Ketuk untuk menyiapkan."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering dinonaktifkan"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Hubungi admin untuk mengetahui detailnya"</string>
<string name="back_button_label" msgid="4078224038025043387">"Kembali"</string>
<string name="next_button_label" msgid="6040209156399907780">"Selanjutnya"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Lewati"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 0f4d6de..d23cf5f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Senda"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Akstursforrit er í gangi"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ýttu til að loka akstursforritinu."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Kveikt á tjóðrun eða aðgangsstað"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Ýttu til að setja upp."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Slökkt er á tjóðrun"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
<string name="back_button_label" msgid="4078224038025043387">"Til baka"</string>
<string name="next_button_label" msgid="6040209156399907780">"Áfram"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Sleppa"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f14e858..8ebcb0e 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Invia"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"App di guida in esecuzione"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tocca per uscire dall\'app di guida."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering oppure hotspot attivo"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tocca per impostare."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering disattivato"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
<string name="back_button_label" msgid="4078224038025043387">"Indietro"</string>
<string name="next_button_label" msgid="6040209156399907780">"Avanti"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Ignora"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 375d7d1..85d0f3b 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"שלח"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"אפליקציית הנהיגה פועלת"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"יש להקיש כדי לצאת מאפליקציית הנהיגה."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"שיתוף אינטרנט פעיל"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"הקש כדי להגדיר."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"שיתוף האינטרנט בין ניידים מושבת"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"לפרטים, יש לפנות למנהל המערכת"</string>
<string name="back_button_label" msgid="4078224038025043387">"הקודם"</string>
<string name="next_button_label" msgid="6040209156399907780">"הבא"</string>
<string name="skip_button_label" msgid="3566599811326688389">"דילוג"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eb990ec..e21035f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"送信"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"運転アプリを実行しています"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"運転アプリを終了するにはタップしてください。"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"テザリングまたはアクセスポイントが有効です"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"タップしてセットアップします。"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"テザリングは無効に設定されています"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"詳しくは、管理者にお問い合わせください"</string>
<string name="back_button_label" msgid="4078224038025043387">"戻る"</string>
<string name="next_button_label" msgid="6040209156399907780">"次へ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"スキップ"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index d2d1fed..941dbbd 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"გაგზავნა"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"მართვის აპი გაშვებულია"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"შეეხეთ მართვის აპიდან გასასვლელად."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"შეეხეთ დასაყენებლად."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ტეტერინგი გათიშულია"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
<string name="back_button_label" msgid="4078224038025043387">"უკან"</string>
<string name="next_button_label" msgid="6040209156399907780">"მომდევნო"</string>
<string name="skip_button_label" msgid="3566599811326688389">"გამოტოვება"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index a1002f7..b142e58 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Жіберу"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Көлік жүргізу қолданбасы қосулы"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Көлік жүргізу қолданбасынан шығу үшін түртіңіз."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Тетеринг немесе хотспот қосулы"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Реттеу үшін түртіңіз."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Тетеринг өшірілді"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Мәліметтерді әкімшіден алыңыз"</string>
<string name="back_button_label" msgid="4078224038025043387">"Артқа"</string>
<string name="next_button_label" msgid="6040209156399907780">"Келесі"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Өткізіп жіберу"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 61a3ec6..43289eb 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1431,10 +1431,6 @@
<string name="submit" msgid="862795280643405865">"ដាក់ស្នើ"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"កម្មវិធីបើកបរកំពុងដំណើរការ"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ចុចដើម្បីចាកចេញពីកម្មវិធីបើកបរ។"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ប៉ះដើម្បីកំណត់"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ការភ្ជាប់ត្រូវបានបិទ"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នកសម្រាប់ព័ត៌មានលម្អិត"</string>
<string name="back_button_label" msgid="4078224038025043387">"ថយក្រោយ"</string>
<string name="next_button_label" msgid="6040209156399907780">"បន្ទាប់"</string>
<string name="skip_button_label" msgid="3566599811326688389">"រំលង"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 5524d40..6ebe7b4 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ಸಲ್ಲಿಸು"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ಡ್ರೈವಿಂಗ್ ಅಪ್ಲಿಕೇಶನ್ ಚಾಲನೆಯಲ್ಲಿದೆ"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ಡ್ರೈವಿಂಗ್ ಅಪ್ಲಿಕೇಶನ್ ನಿರ್ಗಮಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
<string name="back_button_label" msgid="4078224038025043387">"ಹಿಂದೆ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ಮುಂದಿನದು"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ಸ್ಕಿಪ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 423dbd6..c6caedf 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"제출"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"운전 앱 실행 중"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"운전 앱을 종료하려면 탭하세요."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"테더링 또는 핫스팟 사용"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"설정하려면 탭하세요."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"테더링이 사용 중지됨"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"자세한 정보는 관리자에게 문의하세요."</string>
<string name="back_button_label" msgid="4078224038025043387">"뒤로"</string>
<string name="next_button_label" msgid="6040209156399907780">"다음"</string>
<string name="skip_button_label" msgid="3566599811326688389">"건너뛰기"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index d1d773c..341c996 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Тапшыруу"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Унаа айдоо колдонмосу иштеп жатат"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Унаа айдоо колдонмосунан чыгуу үчүн таптаңыз."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Жалгаштыруу же хотспот жандырылган"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Жөндөө үчүн таптап коюңуз."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Жалгаштыруу функциясы өчүрүлгөн"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
<string name="back_button_label" msgid="4078224038025043387">"Артка"</string>
<string name="next_button_label" msgid="6040209156399907780">"Кийинки"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Өткөрүп жиберүү"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ded2a4f..e0da206 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ສົ່ງຂໍ້ມູນ"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ແອັບຂັບລົດກຳລັງເຮັດວຽກຢູ່"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ແຕະເພື່ອອອກຈາກແອັບຂັບລົດ."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ເປີດການປ່ອຍສັນຍານ ຫຼືຮັອດສະປອດແລ້ວ"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
<string name="back_button_label" msgid="4078224038025043387">"ກັບຄືນ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ຕໍ່ໄປ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ຂ້າມ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index eac535c..1212aa4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Pateikti"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Veikia vairavimo programa"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Palieskite, kad išeitumėte iš vairavimo programos."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Susietas ar aktyvus"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Palieskite, kad nustatytumėte."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Įrenginio kaip modemo naudojimas išjungtas"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
<string name="back_button_label" msgid="4078224038025043387">"Atgal"</string>
<string name="next_button_label" msgid="6040209156399907780">"Kitas"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Praleisti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f586212..547b6c9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1449,10 +1449,6 @@
<string name="submit" msgid="862795280643405865">"Iesniegt"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Autovadīšanas lietotne darbojas"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Pieskarieties, lai izietu no autovadīšanas lietotnes"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Piesaiste vai tīklājs ir aktīvs."</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Pieskarieties, lai iestatītu."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Piesaiste ir atspējota"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
<string name="back_button_label" msgid="4078224038025043387">"Atpakaļ"</string>
<string name="next_button_label" msgid="6040209156399907780">"Tālāk"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Izlaist"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0d308a1..93f2da1 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Поднеси"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Апликацијата за возење работи"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Допрете за да излезете од апликацијата за возење."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Поврзувањето или точката на пристап се активни"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Допрете за поставување."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Врзувањето е оневозможено"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Контактирајте со администраторот за детали"</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Следно"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Прескокни"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 63a7194..c220cdd 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"സമർപ്പിക്കുക"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ഡ്രൈവിംഗ് ആപ്പ് റൺ ചെയ്യുകയാണ്"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ഡ്രൈവിംഗ് ആപ്പിൽ നിന്ന് പുറത്തുകടക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്പോട്ട് സജീവമാണ്"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
<string name="back_button_label" msgid="4078224038025043387">"മടങ്ങുക"</string>
<string name="next_button_label" msgid="6040209156399907780">"അടുത്തത്"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ഒഴിവാക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b3cf5ce..8c26113 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Илгээх"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Жолоо барих апп ажиллаж байна"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Жолооны аппаас гарахын тулд товшино уу."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Модем болгох эсвэл идэвхтэй цэг болгох"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Тохируулахын тулд товшино уу."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Модем болгох боломжгүй байна"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
<string name="back_button_label" msgid="4078224038025043387">"Буцах"</string>
<string name="next_button_label" msgid="6040209156399907780">"Дараах"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Алгасах"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 22b1dcc..2782caf 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"सबमिट करा"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्रायव्हिंग अॅप चालू आहे"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्रायव्हिंग ॲपमधून बाहेर पाडण्यासाठी टॅप करा."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"सेट करण्यासाठी टॅप करा."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिंग बंद आहे"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string>
<string name="back_button_label" msgid="4078224038025043387">"मागे"</string>
<string name="next_button_label" msgid="6040209156399907780">"पुढील"</string>
<string name="skip_button_label" msgid="3566599811326688389">"वगळा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 88c12b6..a40eca0 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Serah"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Apl memandu sedang berjalan"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Ketik untuk keluar daripada apl memandu."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Penambatan atau titik panas aktif"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Ketik untuk membuat persediaan."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Penambatan dilumpuhkan"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Hubungi pentadbir anda untuk maklumat lanjut"</string>
<string name="back_button_label" msgid="4078224038025043387">"Kembali"</string>
<string name="next_button_label" msgid="6040209156399907780">"Seterusnya"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Langkau"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index cdf68cd..64e2376 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ပေးပို့ရန်"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ကားမောင်းသည့်အက်ပ် ပွင့်နေပါသည်"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ကားမောင်းသည့်အက်ပ်မှ ထွက်ရန် တို့ပါ။"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
<string name="back_button_label" msgid="4078224038025043387">"နောက်သို့"</string>
<string name="next_button_label" msgid="6040209156399907780">"ရှေ့သို့"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ကျော်ရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b129c3c..69e341e 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Send inn"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Kjøreappen kjører"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Trykk for å lukke kjøreappen."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Internettdeling eller trådløs sone er aktiv"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Trykk for å konfigurere."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Internettdeling er slått av"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Ta kontakt med administratoren din for å få mer informasjon"</string>
<string name="back_button_label" msgid="4078224038025043387">"Tilbake"</string>
<string name="next_button_label" msgid="6040209156399907780">"Neste"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Hopp over"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 648f47c..0fb2213 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1435,10 +1435,6 @@
<string name="submit" msgid="862795280643405865">"पेस गर्नुहोस्"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ड्राइभिङ अनुप्रयोग चलिरहेको छ"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ड्राइभिङ अनुप्रयोगबाट बाहिर निस्कन ट्याप गर्नुहोस्।"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"टेदरिङलाई असक्षम पारिएको छ"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
<string name="back_button_label" msgid="4078224038025043387">"पछाडि"</string>
<string name="next_button_label" msgid="6040209156399907780">"अर्को"</string>
<string name="skip_button_label" msgid="3566599811326688389">"छोड्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e248ca1..363c3c3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Verzenden"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Auto-app wordt uitgevoerd"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tik om de auto-app te sluiten."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering of hotspot actief"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tik om in te stellen."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering is uitgeschakeld"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Neem contact op met je beheerder voor meer informatie"</string>
<string name="back_button_label" msgid="4078224038025043387">"Vorige"</string>
<string name="next_button_label" msgid="6040209156399907780">"Volgende"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Overslaan"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 7a73cc6..566d778 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ଦାଖଲ କରନ୍ତୁ"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ଡ୍ରାଇଭିଙ୍ଗ ଆପ୍ ଚାଲୁଛି"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ଡ୍ରାଇଭିଙ୍ଗ ଆପ୍ରୁ ବାହାରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ସେଟଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
<string name="back_button_label" msgid="4078224038025043387">"ଫେରନ୍ତୁ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ପରବର୍ତ୍ତୀ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ଛାଡ଼ିଦିଅନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 3a245e3..7760db0 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ਪ੍ਰਸਤੁਤ ਕਰੋ"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ਗੱਡੀ ਚਲਾਉਣ ਸੰਬੰਧੀ ਐਪ ਚੱਲ ਰਹੀ ਹੈ"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ਗੱਡੀ ਚਲਾਉਣ ਸੰਬੰਧੀ ਐਪ ਤੋਂ ਬਾਹਰ ਜਾਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
<string name="back_button_label" msgid="4078224038025043387">"ਪਿੱਛੇ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ਅੱਗੇ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ਛੱਡੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 7a86bd5..4ff7cc4 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Prześlij"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Tryb samochodowy jest włączony"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Kliknij, by zakończyć tryb samochodowy."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Aktywny tethering lub punkt dostępu"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Kliknij, by skonfigurować."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering został wyłączony"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
<string name="back_button_label" msgid="4078224038025043387">"Wróć"</string>
<string name="next_button_label" msgid="6040209156399907780">"Dalej"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Pomiń"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 3f82a80..1750d2a 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"O app para carro está sendo usado"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair do app para carro."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Ponto de acesso ou tethering ativo"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering desativado"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Fale com seu administrador para saber detalhes"</string>
<string name="back_button_label" msgid="4078224038025043387">"Voltar"</string>
<string name="next_button_label" msgid="6040209156399907780">"Avançar"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index f67c96f..5c21c0d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"A aplicação de condução está em execução."</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair da aplicação de condução."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Ligação ponto a ponto ou hotspot activos"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"A ligação (à Internet) via telemóvel está desativada."</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contacte o gestor para obter detalhes."</string>
<string name="back_button_label" msgid="4078224038025043387">"Anterior"</string>
<string name="next_button_label" msgid="6040209156399907780">"Seguinte"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Ignorar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3f82a80..1750d2a 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Enviar"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"O app para carro está sendo usado"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Toque para sair do app para carro."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Ponto de acesso ou tethering ativo"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Toque para configurar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering desativado"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Fale com seu administrador para saber detalhes"</string>
<string name="back_button_label" msgid="4078224038025043387">"Voltar"</string>
<string name="next_button_label" msgid="6040209156399907780">"Avançar"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Pular"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 846b982..397525a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1449,10 +1449,6 @@
<string name="submit" msgid="862795280643405865">"Trimiteți"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicația pentru condus rulează"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atingeți ca să ieșiți din aplicația pentru condus."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering sau hotspot activ"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Atingeți ca să configurați."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tetheringul este dezactivat"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Contactați administratorul pentru detalii"</string>
<string name="back_button_label" msgid="4078224038025043387">"Înapoi"</string>
<string name="next_button_label" msgid="6040209156399907780">"Înainte"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Omiteți"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 19fde9f..a7abbff 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Отправить"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Приложение для вождения включено"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Нажмите, чтобы выйти из приложения для вождения."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Включен режим модема"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Нажмите, чтобы настроить."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Включить режим модема нельзя"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Обратитесь к администратору, чтобы узнать подробности."</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Далее"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Пропустить"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 676b48f..7d3f80d 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1431,10 +1431,6 @@
<string name="submit" msgid="862795280643405865">"යොමු කරන්න"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"රියදුරු යෙදුම ධාවනය වේ."</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"රියදුරු යෙදුමෙන් පිටවීම සඳහා තට්ටු කරන්න."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ටෙදරින් හෝ හොට්ස්පොට් සක්රීයයි"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"පිහිටුවීමට තට්ටු කරන්න."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ටෙදරින් අබල කර ඇත"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
<string name="back_button_label" msgid="4078224038025043387">"ආපසු"</string>
<string name="next_button_label" msgid="6040209156399907780">"මීලඟ"</string>
<string name="skip_button_label" msgid="3566599811326688389">"මඟ හරින්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cde5466..3a81468c 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Odoslať"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikácia na šoférovanie je spustená"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Klepnutím ukončíte aplikáciu na šoférovanie"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering alebo prístupový bod je aktívny"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Klepnutím prejdete na nastavenie."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering je deaktivovaný"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"O podrobnosti požiadajte svojho správcu"</string>
<string name="back_button_label" msgid="4078224038025043387">"Späť"</string>
<string name="next_button_label" msgid="6040209156399907780">"Ďalej"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Preskočiť"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0494068..c28384c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Pošlji"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacija za vožnjo se izvaja"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Dotaknite se, če želite zapreti aplikacijo za vožnjo."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Dotaknite se, če želite nastaviti."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Za podrobnosti se obrnite na skrbnika"</string>
<string name="back_button_label" msgid="4078224038025043387">"Nazaj"</string>
<string name="next_button_label" msgid="6040209156399907780">"Naprej"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Preskoči"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 20e6ae5..39f7377 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Dërgo"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplikacioni i drejtimit të makinës është në ekzekutim"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Trokit për të dalë nga aplikacioni i drejtimit të makinës."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Trokit për ta konfiguruar."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Lidhja e çiftimit është çaktivizuar"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakto me administratorin për detaje"</string>
<string name="back_button_label" msgid="4078224038025043387">"Prapa"</string>
<string name="next_button_label" msgid="6040209156399907780">"Përpara"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Kapërce"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index db53b23..e93c852 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1449,10 +1449,6 @@
<string name="submit" msgid="862795280643405865">"Пошаљи"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Апликација за вожњу је покренута"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Додирните да бисте изашли из апликације за вожњу."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Додирните да бисте подесили."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Привезивање је онемогућено"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Потражите детаље од администратора"</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Next"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Прескочи"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 9e99aec..cf0e0bb 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Skicka"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Bilkörningsappen körs"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Tryck här om du vill avsluta bilkörningsappen."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Internetdelning eller surfzon aktiverad"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Tryck om du vill konfigurera."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Internetdelning har inaktiverats"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Kontakta administratören om du vill veta mer"</string>
<string name="back_button_label" msgid="4078224038025043387">"Tillbaka"</string>
<string name="next_button_label" msgid="6040209156399907780">"Nästa"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Hoppa över"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d7b3555..f8cd19c 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Wasilisha"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Umewasha programu ya kuendesha gari"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Gusa ili ufunge programu ya kuendesha gari."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Kushiriki au kusambaza intaneti kumewashwa"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Gusa ili uweke mipangilio."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Umezima kipengele cha kusambaza mtandao"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
<string name="back_button_label" msgid="4078224038025043387">"Nyuma"</string>
<string name="next_button_label" msgid="6040209156399907780">"Endelea"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Ruka"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 569977c..ac284d3 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"சமர்ப்பி"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"\'வாகனம் ஓட்டும் பயன்முறை’ ஆனில் உள்ளது"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"வாகனம் ஓட்டும் பயன்முறையிலிருந்து வெளியேற, தட்டவும்."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"அமைக்க, தட்டவும்."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
<string name="back_button_label" msgid="4078224038025043387">"முந்தையது"</string>
<string name="next_button_label" msgid="6040209156399907780">"அடுத்து"</string>
<string name="skip_button_label" msgid="3566599811326688389">"தவிர்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 9600172..6641c41 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"సమర్పించు"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"డ్రైవింగ్ యాప్ అమలవుతోంది"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"డ్రైవింగ్ యాప్ నుండి నిష్క్రమించడం కోసం నొక్కండి."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"టీథర్ చేయబడినది లేదా హాట్స్పాట్ సక్రియంగా ఉండేది"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"సెటప్ చేయడానికి నొక్కండి."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"టెథెరింగ్ నిలిపివేయబడింది"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string>
<string name="back_button_label" msgid="4078224038025043387">"వెనుకకు"</string>
<string name="next_button_label" msgid="6040209156399907780">"తర్వాత"</string>
<string name="skip_button_label" msgid="3566599811326688389">"దాటవేయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4dd0d0f..bc4b79e 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"ส่ง"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"แอปสำหรับการขับขี่ทำงานอยู่"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"แตะเพื่อออกจากแอปสำหรับการขับขี่"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"แตะเพื่อตั้งค่า"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
<string name="back_button_label" msgid="4078224038025043387">"กลับ"</string>
<string name="next_button_label" msgid="6040209156399907780">"ถัดไป"</string>
<string name="skip_button_label" msgid="3566599811326688389">"ข้าม"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index bde4027..b24e95a 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Isumite"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Tumatakbo ang driving app"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Mag-tap para lumabas sa app sa pagmamaneho."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Pagsasama o aktibong hotspot"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"I-tap upang i-set up."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Naka-disable ang pag-tether"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
<string name="back_button_label" msgid="4078224038025043387">"Bumalik"</string>
<string name="next_button_label" msgid="6040209156399907780">"Susunod"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Laktawan"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0168ba0..326ace9 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Gönder"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Sürüş uygulaması çalışıyor"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Sürüş uygulamasından çıkmak için dokunun."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Tethering veya hotspot etkin"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Ayarlamak için dokunun."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Tethering devre dışı bırakıldı"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
<string name="back_button_label" msgid="4078224038025043387">"Geri"</string>
<string name="next_button_label" msgid="6040209156399907780">"İleri"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Atla"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 32042ef..8007f9e 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1469,10 +1469,6 @@
<string name="submit" msgid="862795280643405865">"Надіслати"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Працює додаток для автомобілів"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Торкніться, щоб вийти з додатка для автомобілів."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Прив\'язка чи точка дост. активна"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Торкніться, щоб налаштувати."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Використання телефона в режимі модема вимкнено"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string>
<string name="back_button_label" msgid="4078224038025043387">"Назад"</string>
<string name="next_button_label" msgid="6040209156399907780">"Далі"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Пропустити"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 1dd8911..312aed7d 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"جمع کرائیں"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"ڈرائیونگ ایپ چل رہی ہے"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"ڈرائیونگ ایپ سے باہر نکلنے کے لئے تھپتھپائيں۔"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"ٹیدرنگ غیر فعال ہے"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
<string name="back_button_label" msgid="4078224038025043387">"واپس جائیں"</string>
<string name="next_button_label" msgid="6040209156399907780">"اگلا"</string>
<string name="skip_button_label" msgid="3566599811326688389">"نظر انداز کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 4b7847d..c7987d1 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Yuborish"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Avtomobil ilovasi ishlayapti"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Avtomobil ilovasidan chiqish uchun bosing"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Modem rejimi yoniq"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Sozlash uchun bosing."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Modem rejimi faolsizlantirildi"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
<string name="back_button_label" msgid="4078224038025043387">"Orqaga"</string>
<string name="next_button_label" msgid="6040209156399907780">"Keyingisi"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Tashlab o‘tish"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 971cf34..13c75f6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Gửi"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Ứng dụng lái xe đang chạy"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Nhấn để thoát khỏi ứng dụng lái xe."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Nhấn để thiết lập."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Đã tắt tính năng chia sẻ kết nối"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
<string name="back_button_label" msgid="4078224038025043387">"Quay lại"</string>
<string name="next_button_label" msgid="6040209156399907780">"Tiếp theo"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Bỏ qua"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index d7dd8b1..1df2a40 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"提交"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"驾驶应用正在运行"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"点按即可退出驾驶应用。"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"网络共享或热点已启用"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"点按即可进行设置。"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"网络共享已停用"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"请与您的管理员联系以了解详情"</string>
<string name="back_button_label" msgid="4078224038025043387">"上一步"</string>
<string name="next_button_label" msgid="6040209156399907780">"下一步"</string>
<string name="skip_button_label" msgid="3566599811326688389">"跳过"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 1a37844..d4d1f7c 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"提交"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"駕駛應用程式執行中"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"輕按即可退出駕駛應用程式。"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"已啟用網絡共享或熱點"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"輕按即可設定。"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"網絡共享已停用"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"請聯絡您的管理員以瞭解詳情"</string>
<string name="back_button_label" msgid="4078224038025043387">"返回"</string>
<string name="next_button_label" msgid="6040209156399907780">"繼續"</string>
<string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5e655ac..9997b68 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"提交"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"行車應用程式執行中"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"輕觸即可結束行車應用程式。"</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"網路共用或無線基地台已啟用"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"輕觸即可進行設定。"</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"數據連線已停用"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"詳情請洽你的管理員"</string>
<string name="back_button_label" msgid="4078224038025043387">"返回"</string>
<string name="next_button_label" msgid="6040209156399907780">"繼續"</string>
<string name="skip_button_label" msgid="3566599811326688389">"略過"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5f8d61e..5838a93 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1429,10 +1429,6 @@
<string name="submit" msgid="862795280643405865">"Hambisa"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Uhlelo lokusebenza lokushayela luyasebenza"</string>
<string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Thepha ukuze uphume kuhlelo lokusebenza lokushayela."</string>
- <string name="tethered_notification_title" msgid="2700523927485687353">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
- <string name="tethered_notification_message" msgid="6228080755828019453">"Thepha ukuze usethe."</string>
- <string name="disable_tether_notification_title" msgid="7158047514545848391">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
- <string name="disable_tether_notification_message" msgid="98281313984014775">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
<string name="back_button_label" msgid="4078224038025043387">"Emuva"</string>
<string name="next_button_label" msgid="6040209156399907780">"Okulandelayo"</string>
<string name="skip_button_label" msgid="3566599811326688389">"Yeqa"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6741fea..76d9561 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2363,9 +2363,11 @@
1 (0b001) - enforce (only install system packages if they are whitelisted)
2 (0b010) - log (log when a non-whitelisted package is run)
4 (0b100) - treat any package not mentioned in the whitelist file as implicitly whitelisted
+ 8 (0b1000) - ignore OTAs (don't install system packages during OTAs)
Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in
frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java -->
- <integer name="config_userTypePackageWhitelistMode">5</integer> <!-- 0b101 -->
+ <integer name="config_userTypePackageWhitelistMode">13</integer> <!-- 0b1101 -->
+ <!-- TODO(b/143200798): Change to value 5, i.e. 0b0101, when b/143200798 is resolved. -->
<!-- Whether UI for multi user should be shown -->
<bool name="config_enableMultiUserUI">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2d31f49..dcf7379 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3838,18 +3838,6 @@
<string name="car_mode_disable_notification_title">Driving app is running</string>
<string name="car_mode_disable_notification_message">Tap to exit driving app.</string>
- <!-- Strings for tethered notification -->
- <!-- Shown when the device is tethered -->
- <string name="tethered_notification_title">Tethering or hotspot active</string>
- <string name="tethered_notification_message">Tap to set up.</string>
-
- <!-- Strings for tether disabling notification -->
- <!-- This notification is shown when tethering has been disabled on a user's device.
- The device is managed by the user's employer. Tethering can't be turned on unless the
- IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
- <string name="disable_tether_notification_title">Tethering is disabled</string>
- <string name="disable_tether_notification_message">Contact your admin for details</string>
-
<!-- Strings for possible PreferenceActivity Back/Next buttons -->
<string name="back_button_label">Back</string>
<string name="next_button_label">Next</string>
@@ -5336,5 +5324,5 @@
<string name="accessibility_system_action_screenshot_label">Screenshot</string>
<!-- Accessibility description of caption view -->
- <string name="accessibility_freeform_caption"><xliff:g id="app_name">%1$s</xliff:g> app in Pop-up window.</string>
+ <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 130b31f..d15d4d6 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1893,9 +1893,6 @@
<java-symbol type="drawable" name="stat_sys_battery_charge" />
<java-symbol type="drawable" name="stat_sys_battery_unknown" />
<java-symbol type="drawable" name="stat_sys_data_usb" />
- <java-symbol type="drawable" name="stat_sys_tether_bluetooth" />
- <java-symbol type="drawable" name="stat_sys_tether_general" />
- <java-symbol type="drawable" name="stat_sys_tether_usb" />
<java-symbol type="drawable" name="stat_sys_throttled" />
<java-symbol type="drawable" name="vpn_connected" />
<java-symbol type="drawable" name="vpn_disconnected" />
@@ -2071,10 +2068,6 @@
<java-symbol type="string" name="select_keyboard_layout_notification_message" />
<java-symbol type="string" name="smv_application" />
<java-symbol type="string" name="smv_process" />
- <java-symbol type="string" name="tethered_notification_message" />
- <java-symbol type="string" name="tethered_notification_title" />
- <java-symbol type="string" name="disable_tether_notification_message" />
- <java-symbol type="string" name="disable_tether_notification_title" />
<java-symbol type="string" name="adb_debugging_notification_channel_tv" />
<java-symbol type="string" name="usb_accessory_notification_title" />
<java-symbol type="string" name="usb_mtp_notification_title" />
diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
new file mode 100644
index 0000000..ae91edc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 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.app.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.junit.Test;
+
+public class PhoneTimeZoneSuggestionTest {
+ private static final int PHONE_ID = 99999;
+
+ @Test
+ public void testEquals() {
+ PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ assertEquals(one, one);
+ }
+
+ PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertEquals(one, two);
+ assertEquals(two, one);
+ }
+
+ PhoneTimeZoneSuggestion.Builder builder3 =
+ new PhoneTimeZoneSuggestion.Builder(PHONE_ID + 1);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion three = builder3.build();
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
+
+ builder1.setZoneId("Europe/London");
+ builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+ builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertNotEquals(one, two);
+ }
+
+ builder2.setZoneId("Europe/Paris");
+ builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+ builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertNotEquals(one, two);
+ }
+
+ builder1.setZoneId("Europe/Paris");
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertEquals(one, two);
+ }
+
+ builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
+ builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertNotEquals(one, two);
+ }
+
+ builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertEquals(one, two);
+ }
+
+ builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+ builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertNotEquals(one, two);
+ }
+
+ builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS);
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ assertEquals(one, two);
+ }
+
+ // DebugInfo must not be considered in equals().
+ {
+ PhoneTimeZoneSuggestion one = builder1.build();
+ PhoneTimeZoneSuggestion two = builder2.build();
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testBuilderValidates_emptyZone_badMatchType() {
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ // No zone ID, so match type should be left unset.
+ builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET);
+ builder.build();
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testBuilderValidates_zoneSet_badMatchType() {
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ builder.setZoneId("Europe/London");
+ builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+ builder.build();
+ }
+
+ @Test
+ public void testParcelable() {
+ PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(PHONE_ID);
+ assertRoundTripParcelable(builder.build());
+
+ builder.setZoneId("Europe/London");
+ builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID);
+ builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE);
+ PhoneTimeZoneSuggestion suggestion1 = builder.build();
+ assertRoundTripParcelable(suggestion1);
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ String debugString = "This is debug info";
+ suggestion1.addDebugInfo(debugString);
+ PhoneTimeZoneSuggestion suggestion1_2 = roundTripParcelable(suggestion1);
+ assertEquals(suggestion1, suggestion1_2);
+ assertTrue(suggestion1_2.getDebugInfo().contains(debugString));
+ }
+
+ private static void assertRoundTripParcelable(PhoneTimeZoneSuggestion instance) {
+ assertEquals(instance, roundTripParcelable(instance));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T extends Parcelable> T roundTripParcelable(T one) {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeTypedObject(one, 0);
+ parcel.setDataPosition(0);
+
+ T toReturn = (T) parcel.readTypedObject(PhoneTimeZoneSuggestion.CREATOR);
+ parcel.recycle();
+ return toReturn;
+ }
+}
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
new file mode 100644
index 0000000..613ce90
--- /dev/null
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 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.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Shader that calculates pixel output with a program (fragment shader) running on a GPU.
+ * @hide
+ */
+public class RuntimeShader extends Shader {
+
+ private static class NoImagePreloadHolder {
+ public static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ private byte[] mUniforms;
+
+ /**
+ * Current native shader factory instance.
+ */
+ private long mNativeInstanceRuntimeShaderFactory;
+
+ /**
+ * Creates a new RuntimeShader.
+ *
+ * @param sksl The text of SKSL program to run on the GPU.
+ * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+ * on number of uniforms declared by sksl.
+ * @param isOpaque True if all pixels have alpha 1.0f.
+ */
+ public RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque) {
+ this(sksl, uniforms, isOpaque, ColorSpace.get(ColorSpace.Named.SRGB));
+ }
+
+ private RuntimeShader(@NonNull String sksl, @Nullable byte[] uniforms, boolean isOpaque,
+ ColorSpace colorSpace) {
+ super(colorSpace);
+ mUniforms = uniforms;
+ mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl, isOpaque);
+ NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
+ mNativeInstanceRuntimeShaderFactory);
+ }
+
+ /**
+ * Sets new value for shader parameters.
+ *
+ * @param uniforms Array of parameters passed by the SKSL shader. Array size depends
+ * on number of uniforms declared by mSksl.
+ */
+ public void updateUniforms(@Nullable byte[] uniforms) {
+ mUniforms = uniforms;
+ discardNativeInstance();
+ }
+
+ @Override
+ long createNativeInstance(long nativeMatrix) {
+ return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
+ colorSpace().getNativeInstance());
+ }
+
+ private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
+ long colorSpaceHandle);
+
+ private static native long nativeCreateShaderFactory(String sksl, boolean isOpaque);
+
+ private static native long nativeGetFinalizer();
+}
+
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index f25910b..572fa8c 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -68,6 +68,9 @@
/** Key prefix for WIFI. */
public static final String WIFI = "WIFI_";
+ /** Key prefix for App Source certificates. */
+ public static final String APP_SOURCE_CERTIFICATE = "FSV_";
+
/** Key containing suffix of lockdown VPN profile. */
public static final String LOCKDOWN_VPN = "LOCKDOWN_VPN";
@@ -80,6 +83,9 @@
/** Name of WIFI certificate usage. */
public static final String CERTIFICATE_USAGE_WIFI = "wifi";
+ /** Name of App Source certificate usage. */
+ public static final String CERTIFICATE_USAGE_APP_SOURCE = "appsrc";
+
/** Data type for public keys. */
public static final String EXTRA_PUBLIC_KEY = "KEY";
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 2f1eeda..89a3bc0 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -34,7 +34,7 @@
*/
public final class GnssStatus {
- // these must match the definitions in gps.h
+ // These must match the definitions in GNSS HAL.
//
// Note: these constants are also duplicated in GnssStatusCompat.java in the androidx support
// library. if adding a constellation, please update that file as well.
@@ -63,9 +63,10 @@
private static final int SVID_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
private static final int SVID_FLAGS_USED_IN_FIX = (1 << 2);
private static final int SVID_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
+ private static final int SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4);
- private static final int SVID_SHIFT_WIDTH = 8;
- private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+ private static final int SVID_SHIFT_WIDTH = 12;
+ private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 8;
private static final int CONSTELLATION_TYPE_MASK = 0xf;
/**
@@ -123,9 +124,10 @@
*/
@NonNull
public static GnssStatus wrap(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
- float[] elevations, float[] azimuths, float[] carrierFrequencies) {
+ float[] elevations, float[] azimuths, float[] carrierFrequencies,
+ float[] basebandCn0DbHzs) {
return new GnssStatus(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
- carrierFrequencies);
+ carrierFrequencies, basebandCn0DbHzs);
}
private final int mSvCount;
@@ -134,15 +136,17 @@
private final float[] mElevations;
private final float[] mAzimuths;
private final float[] mCarrierFrequencies;
+ private final float[] mBasebandCn0DbHzs;
private GnssStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs, float[] elevations,
- float[] azimuths, float[] carrierFrequencies) {
+ float[] azimuths, float[] carrierFrequencies, float[] basebandCn0DbHzs) {
mSvCount = svCount;
mSvidWithFlags = svidWithFlags;
mCn0DbHzs = cn0DbHzs;
mElevations = elevations;
mAzimuths = azimuths;
mCarrierFrequencies = carrierFrequencies;
+ mBasebandCn0DbHzs = basebandCn0DbHzs;
}
/**
@@ -284,6 +288,26 @@
}
/**
+ * Reports whether a valid {@link #getBasebandCn0DbHz(int satelliteIndex)} is available.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean hasBasebandCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_BASEBAND_CN0) != 0;
+ }
+
+ /**
+ * Retrieves the baseband carrier-to-noise density of the satellite at the specified index in
+ * dB-Hz.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = 0, to = 63)
+ public float getBasebandCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return mBasebandCn0DbHzs[satelliteIndex];
+ }
+
+ /**
* Returns the string representation of a constellation type.
*
* @param constellationType the constellation type.
@@ -334,6 +358,8 @@
* @param usedInFix whether the satellite was used in the most recent location fix
* @param hasCarrierFrequency whether carrier frequency data is available
* @param carrierFrequency satellite carrier frequency in Hz
+ * @param hasBasebandCn0DbHz whether baseband carrier-to-noise density is available
+ * @param basebandCn0DbHz baseband carrier-to-noise density in dB-Hz
*/
@NonNull
public Builder addSatellite(@ConstellationType int constellationType,
@@ -345,9 +371,12 @@
boolean hasAlmanac,
boolean usedInFix,
boolean hasCarrierFrequency,
- @FloatRange(from = 0) float carrierFrequency) {
+ @FloatRange(from = 0) float carrierFrequency,
+ boolean hasBasebandCn0DbHz,
+ @FloatRange(from = 0, to = 63) float basebandCn0DbHz) {
mSatellites.add(new GnssSvInfo(constellationType, svid, cn0DbHz, elevation, azimuth,
- hasEphemeris, hasAlmanac, usedInFix, hasCarrierFrequency, carrierFrequency));
+ hasEphemeris, hasAlmanac, usedInFix, hasCarrierFrequency, carrierFrequency,
+ hasBasebandCn0DbHz, basebandCn0DbHz));
return this;
}
@@ -371,6 +400,7 @@
float[] elevations = new float[svCount];
float[] azimuths = new float[svCount];
float[] carrierFrequencies = new float[svCount];
+ float[] basebandCn0DbHzs = new float[svCount];
for (int i = 0; i < svidWithFlags.length; i++) {
svidWithFlags[i] = mSatellites.get(i).mSvidWithFlags;
@@ -387,9 +417,12 @@
for (int i = 0; i < carrierFrequencies.length; i++) {
carrierFrequencies[i] = mSatellites.get(i).mCarrierFrequency;
}
+ for (int i = 0; i < basebandCn0DbHzs.length; i++) {
+ basebandCn0DbHzs[i] = mSatellites.get(i).mBasebandCn0DbHz;
+ }
return wrap(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
- carrierFrequencies);
+ carrierFrequencies, basebandCn0DbHzs);
}
}
@@ -400,21 +433,25 @@
private final float mElevation;
private final float mAzimuth;
private final float mCarrierFrequency;
+ private final float mBasebandCn0DbHz;
private GnssSvInfo(int constellationType, int svid, float cn0DbHz,
float elevation, float azimuth, boolean hasEphemeris, boolean hasAlmanac,
- boolean usedInFix, boolean hasCarrierFrequency, float carrierFrequency) {
+ boolean usedInFix, boolean hasCarrierFrequency, float carrierFrequency,
+ boolean hasBasebandCn0DbHz, float basebandCn0DbHz) {
mSvidWithFlags = (svid << SVID_SHIFT_WIDTH)
| ((constellationType & CONSTELLATION_TYPE_MASK)
<< CONSTELLATION_TYPE_SHIFT_WIDTH)
| (hasEphemeris ? SVID_FLAGS_HAS_EPHEMERIS_DATA : SVID_FLAGS_NONE)
| (hasAlmanac ? SVID_FLAGS_HAS_ALMANAC_DATA : SVID_FLAGS_NONE)
| (usedInFix ? SVID_FLAGS_USED_IN_FIX : SVID_FLAGS_NONE)
- | (hasCarrierFrequency ? SVID_FLAGS_HAS_CARRIER_FREQUENCY : SVID_FLAGS_NONE);
+ | (hasCarrierFrequency ? SVID_FLAGS_HAS_CARRIER_FREQUENCY : SVID_FLAGS_NONE)
+ | (hasBasebandCn0DbHz ? SVID_FLAGS_HAS_BASEBAND_CN0 : SVID_FLAGS_NONE);
mCn0DbHz = cn0DbHz;
mElevation = elevation;
mAzimuth = azimuth;
mCarrierFrequency = carrierFrequency;
+ mBasebandCn0DbHz = basebandCn0DbHz;
}
}
}
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index d824cb1..1931a00 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -28,6 +28,6 @@
void onFirstFix(int ttff);
void onSvStatusChanged(int svCount, in int[] svidWithFlags, in float[] cn0s,
in float[] elevations, in float[] azimuths,
- in float[] carrierFreqs);
+ in float[] carrierFreqs, in float[] basebandCn0s);
void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 75e1cd4..c004172 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2818,9 +2818,10 @@
@Override
public void onSvStatusChanged(int svCount, int[] svidWithFlags, float[] cn0s,
- float[] elevations, float[] azimuths, float[] carrierFreqs) {
+ float[] elevations, float[] azimuths, float[] carrierFreqs,
+ float[] basebandCn0s) {
GnssStatus localStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0s,
- elevations, azimuths, carrierFreqs);
+ elevations, azimuths, carrierFreqs, basebandCn0s);
mGnssStatus = localStatus;
execute((callback) -> callback.onSatelliteStatusChanged(localStatus));
}
diff --git a/location/tests/locationtests/src/android/location/GnssStatusTest.java b/location/tests/locationtests/src/android/location/GnssStatusTest.java
deleted file mode 100644
index 79ea0d6..0000000
--- a/location/tests/locationtests/src/android/location/GnssStatusTest.java
+++ /dev/null
@@ -1,130 +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 android.location;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-import java.lang.reflect.Constructor;
-import java.util.ArrayList;
-import java.util.List;
-import junit.framework.TestCase;
-
-/**
- * Unit tests for {@link GnssStatus}.
- */
-@SmallTest
-public class GnssStatusTest extends TestCase {
-
- private static final String TAG = GnssStatusTest.class.getSimpleName();
- public void setUp() throws Exception {
- super.setUp();
- }
-
- /*
- * Create {@link GnssStatus} with default value, verify whether its fields are set correctly.
- *
- */
- public void testEmptyGnssStatus() throws Exception {
- Log.i(TAG, "testEmptyGnssStatus");
- List<SatelliteInfo> svInfos = new ArrayList<>();
- GnssStatus gnssStatus = createGnssStatus(svInfos);
- verifyGnssStatus(svInfos, gnssStatus);
- }
-
- /*
- * Create {@link GnssStatus} with only one satellite info, verify whether its fields are set
- * correctly.
- */
- public void testOneSatelliteGnssStatus() throws Exception {
- Log.i(TAG, "testOneSatelliteGnssStatus");
- List<SatelliteInfo> svInfos = new ArrayList<>();
- SatelliteInfo svInfo =
- new SatelliteInfo(100,1, true, true, true, true, 100f, 20.3f, 45.5f, 100.23f);
- svInfos.add(svInfo);
- GnssStatus gnssStatus = createGnssStatus(svInfos);
- verifyGnssStatus(svInfos, gnssStatus);
- }
-
- /*
- * Create {@link GnssStatus} with multiple satellite info, verify whether its fields are set
- * correctly.
- */
- public void testMultipleSatellitesGnssStatus() throws Exception {
- Log.i(TAG, "testMultipleSatellitesGnssStatus");
- List<SatelliteInfo> svInfos = new ArrayList<>();
- SatelliteInfo svInfo1 =
- new SatelliteInfo(20, 1,true, true, true, true, 10.1f, 20.3f, 45.5f, 111.23f);
- SatelliteInfo svInfo2 =
- new SatelliteInfo(50, 2, true, false, true, false, 20.2f, 21.3f, 46.5f, 222.23f);
- SatelliteInfo svInfo3 =
- new SatelliteInfo(192, 3, false, true, false, true, 30.3f, 22.3f, 47.5f, 333.23f);
- SatelliteInfo svInfo4 =
- new SatelliteInfo(250, 4, false, false, false, false, 40.4f, 23.3f, 48.5f, 444.23f);
- svInfos.add(svInfo1);
- svInfos.add(svInfo2);
- svInfos.add(svInfo3);
- svInfos.add(svInfo4);
- GnssStatus gnssStatus = createGnssStatus(svInfos);
- verifyGnssStatus(svInfos, gnssStatus);
- }
-
- private void verifyGnssStatus(List<SatelliteInfo> svInfos, GnssStatus gnssStatus) {
- Log.i(TAG, String.format("Verifing {0} satellites info.",svInfos.size()));
- assertEquals(TAG + "::SatelliteCount", svInfos.size(),
- gnssStatus.getSatelliteCount());
- for (int i = 0; i< svInfos.size(); i++) {
- SatelliteInfo svInfo = svInfos.get(i);
- assertEquals(TAG + "::Svid", svInfo.mSvid, gnssStatus.getSvid(i));
- assertEquals(TAG + "::ConstellationType", svInfo.mConstellationType,
- gnssStatus.getConstellationType(i));
- assertEquals(TAG + "::Cn0DbHz", svInfo.mCn0DbHz, gnssStatus.getCn0DbHz(i));
- assertEquals(TAG + "::Elevation", svInfo.mElevation,
- gnssStatus.getElevationDegrees(i));
- assertEquals(TAG + "::Azimuth", svInfo.mAzimuth, gnssStatus.getAzimuthDegrees(i));
- assertEquals(TAG + "::CarrierFrequencyHz", svInfo.mCarrierFrequency,
- gnssStatus.getCarrierFrequencyHz(i));
- assertEquals(TAG + "::hasEphemerisData", svInfo.mHasEphemris,
- gnssStatus.hasEphemerisData(i));
- assertEquals(TAG + "::HasAlmanacData", svInfo.mHasAlmanac,
- gnssStatus.hasAlmanacData(i));
- assertEquals(TAG + "::UsedInFix", svInfo.mUsedInFix, gnssStatus.usedInFix(i));
- assertEquals(TAG + "::HasCarrierFrequencyHz", svInfo.mHasCarriesFrequency,
- gnssStatus.hasCarrierFrequencyHz(i));
- }
- }
-
- private static GnssStatus createGnssStatus(List<SatelliteInfo> svInfos) throws Exception {
- Class<?> intClass = Integer.TYPE;
- Class<?> floatArrayClass = Class.forName("[F");
- Class<?> intArrayClass = Class.forName("[I");
- Class[] cArg = new Class[6];
- cArg[0] = intClass;
- cArg[1] = intArrayClass;
- cArg[2] = floatArrayClass;
- cArg[3] = floatArrayClass;
- cArg[4] = floatArrayClass;
- cArg[5] = floatArrayClass;
- Constructor<GnssStatus> ctor = GnssStatus.class.getDeclaredConstructor(cArg);
- ctor.setAccessible(true);
- return ctor.newInstance(svInfos.size(),
- SatelliteInfo.getSvidWithFlagsArray(svInfos),
- SatelliteInfo.getCn0sArray(svInfos),
- SatelliteInfo.getElevationsArray(svInfos),
- SatelliteInfo.getAzimuthsArray(svInfos),
- SatelliteInfo.getCarrierFrequencyArray(svInfos));
- }
-}
diff --git a/location/tests/locationtests/src/android/location/SatelliteInfo.java b/location/tests/locationtests/src/android/location/SatelliteInfo.java
deleted file mode 100644
index b6453ef..0000000
--- a/location/tests/locationtests/src/android/location/SatelliteInfo.java
+++ /dev/null
@@ -1,155 +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 android.location;
-
-import java.util.List;
-
-/*
- * Helper class to store single Satellite info, only used it in the unit test.
- */
-public class SatelliteInfo {
- private static final int SVID_MAX_BIT_INDEX = 32;
- private static final int SVID_SHIFT_WIDTH = 8;
- private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
-
- // Index for the bits in mSvidWithFlag
- private static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX = 0;
- private static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX = 1;
- private static final int GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX = 2;
- private static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX = 3;
- public int mSvid;
- public int mSvidWithFlag;
- public float mCn0DbHz;
- public float mElevation;
- public float mAzimuth;
- public float mCarrierFrequency;
-
- /*
- * Flag fields, it stores the same information as svidWithFlag, but in different format, easy for
- * the unit test.
- */
- public int mConstellationType;
- public boolean mHasEphemris;
- public boolean mHasAlmanac;
- public boolean mUsedInFix;
- public boolean mHasCarriesFrequency;
-
- public SatelliteInfo(int svid, int constellationType, boolean hasEphemris, boolean hasAlmanac,
- boolean usedInFix, boolean hasCarriesFrequency, float cn0, float elevation, float azimuth,
- float carrierFrequency) {
- mSvidWithFlag =
- setRange(mSvidWithFlag, constellationType, CONSTELLATION_TYPE_SHIFT_WIDTH, SVID_SHIFT_WIDTH);
- mSvidWithFlag = setRange(mSvidWithFlag, svid, SVID_SHIFT_WIDTH, SVID_MAX_BIT_INDEX);
- mSvidWithFlag = setBit(mSvidWithFlag, hasEphemris, GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA_BIT_INDEX);
- mSvidWithFlag = setBit(mSvidWithFlag, hasAlmanac, GNSS_SV_FLAGS_HAS_ALMANAC_DATA_BIT_INDEX);
- mSvidWithFlag = setBit(mSvidWithFlag, usedInFix, GNSS_SV_FLAGS_USED_IN_FIX_BIT_INDEX);
- mSvidWithFlag =
- setBit(mSvidWithFlag, hasCarriesFrequency, GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY_BIT_INDEX);
- this.mSvid = svid;
- this.mConstellationType = constellationType;
- this.mCn0DbHz = cn0;
- this.mElevation = elevation;
- this.mAzimuth = azimuth;
- this.mCarrierFrequency = carrierFrequency;
- this.mHasEphemris = hasEphemris;
- this.mHasAlmanac = hasAlmanac;
- this.mUsedInFix = usedInFix;
- this.mHasCarriesFrequency = hasCarriesFrequency;
- }
-
- /*
- * Gernerate svidWithFlags array from svInfos
- */
- public static int[] getSvidWithFlagsArray(List<SatelliteInfo> svInfos) {
- int[] svidWithFlags = new int[svInfos.size()];
- for (int i = 0; i< svInfos.size(); i++) {
- svidWithFlags[i] = svInfos.get(i).mSvidWithFlag;
- }
- return svidWithFlags;
- }
-
- /*
- * Gernerate cn0s array from svInfos
- */
- public static float[] getCn0sArray(List<SatelliteInfo> svInfos) {
- float[] cn0s = new float[svInfos.size()];
- for (int i = 0; i< svInfos.size(); i++) {
- cn0s[i] = svInfos.get(i).mCn0DbHz;
- }
- return cn0s;
- }
-
- /*
- * Gernerate elevations array from svInfos
- */
- public static float[] getElevationsArray(List<SatelliteInfo> svInfos) {
- float[] elevations = new float[svInfos.size()];
- for (int i = 0; i< svInfos.size(); i++) {
- elevations[i] = svInfos.get(i).mElevation;
- }
- return elevations;
- }
-
- /*
- * Gernerate azimuths array from svInfos
- */
- public static float[] getAzimuthsArray(List<SatelliteInfo> svInfos) {
- float[] azimuths = new float[svInfos.size()];
- for (int i = 0; i< svInfos.size(); i++) {
- azimuths[i] = svInfos.get(i).mAzimuth;
- }
- return azimuths;
- }
-
- /*
- * Gernerate carrierFrequency array from svInfos
- */
- public static float[] getCarrierFrequencyArray(List<SatelliteInfo> svInfos) {
- float[] carrierFrequencies = new float[svInfos.size()];
- for (int i = 0; i< svInfos.size(); i++) {
- carrierFrequencies[i] = svInfos.get(i).mCarrierFrequency;
- }
- return carrierFrequencies;
- }
-
- private int setBit(int targetValue, boolean value, int index) {
- if (value) {
- targetValue = targetValue | (1 << index);
- } else {
- targetValue = targetValue & ~(1 << index);
- }
- return targetValue;
- }
-
- /*
- * Set the bit in the range [fromIndex, toIndex), index start from the lowest bit.
- * value -> 1 1 0 1 1 0 1 0
- * index -> 7 6 5 4 3 2 1 0
- * This function will set the bit in the range to the lowest X bits of the value.
- */
- private int setRange(int targetValue, int value, int fromIndex, int toIndex) {
- int rangeLen = toIndex - fromIndex;
- int valueMask = (1 << rangeLen) -1;
- value &= valueMask;
- value = value << fromIndex;
- valueMask = valueMask << fromIndex;
- targetValue &= (~valueMask);
- targetValue |= value;
- return targetValue;
- }
-
-}
\ No newline at end of file
diff --git a/media/java/android/media/IMediaRoute2ProviderClient.aidl b/media/java/android/media/IMediaRoute2ProviderClient.aidl
index 6f44d45..f4fb7f4 100644
--- a/media/java/android/media/IMediaRoute2ProviderClient.aidl
+++ b/media/java/android/media/IMediaRoute2ProviderClient.aidl
@@ -17,10 +17,13 @@
package android.media;
import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRoute2Info;
+import android.os.Bundle;
/**
* @hide
*/
oneway interface IMediaRoute2ProviderClient {
void updateProviderInfo(in MediaRoute2ProviderInfo info);
+ void notifyRouteSelected(String packageName, String routeId, in Bundle controlHints, int seq);
}
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 386d2dc..1b6183e 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -18,14 +18,19 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import java.util.Objects;
+
/**
* @hide
*/
@@ -44,7 +49,7 @@
}
@Override
- public IBinder onBind(Intent intent) {
+ public IBinder onBind(@NonNull Intent intent) {
//TODO: Allow binding from media router service only?
if (SERVICE_INTERFACE.equals(intent.getAction())) {
if (mStub == null) {
@@ -57,11 +62,17 @@
/**
* Called when selectRoute is called on a route of the provider.
+ * Once the route is ready to be used , call {@link #notifyRouteSelected(SelectToken, Bundle)}
+ * to notify that.
*
* @param packageName the package name of the application that selected the route
* @param routeId the id of the route being selected
+ * @param token token that contains select info
+ *
+ * @see #notifyRouteSelected
*/
- public abstract void onSelectRoute(String packageName, String routeId);
+ public abstract void onSelectRoute(@NonNull String packageName, @NonNull String routeId,
+ @NonNull SelectToken token);
/**
* Called when unselectRoute is called on a route of the provider.
@@ -69,7 +80,7 @@
* @param packageName the package name of the application that has selected the route.
* @param routeId the id of the route being unselected
*/
- public abstract void onUnselectRoute(String packageName, String routeId);
+ public abstract void onUnselectRoute(@NonNull String packageName, @NonNull String routeId);
/**
* Called when sendControlRequest is called on a route of the provider
@@ -78,21 +89,21 @@
* @param request the media control request intent
*/
//TODO: Discuss what to use for request (e.g., Intent? Request class?)
- public abstract void onControlRequest(String routeId, Intent request);
+ public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
/**
* Called when requestSetVolume is called on a route of the provider
* @param routeId the id of the route
* @param volume the target volume
*/
- public abstract void onSetVolume(String routeId, int volume);
+ public abstract void onSetVolume(@NonNull String routeId, int volume);
/**
* Called when requestUpdateVolume is called on a route of the provider
* @param routeId id of the route
* @param delta the delta to add to the current volume
*/
- public abstract void onUpdateVolume(String routeId, int delta);
+ public abstract void onUpdateVolume(@NonNull String routeId, int delta);
/**
* Updates provider info and publishes routes
@@ -102,6 +113,29 @@
publishState();
}
+ /**
+ * Notifies the client of that the selected route is ready for use. If the selected route can be
+ * controlled, pass a {@link Bundle} that contains how to control it.
+ *
+ * @param token token passed in {@link #onSelectRoute}
+ * @param controlHints a {@link Bundle} that contains how to control the given route.
+ * Pass {@code null} if the route is not available.
+ */
+ public final void notifyRouteSelected(@NonNull SelectToken token,
+ @Nullable Bundle controlHints) {
+ Objects.requireNonNull(token, "token must not be null");
+
+ if (mClient == null) {
+ return;
+ }
+ try {
+ mClient.notifyRouteSelected(token.mPackageName, token.mRouteId,
+ controlHints, token.mSeq);
+ } catch (RemoteException ex) {
+ Log.w(TAG, "Failed to notify route selected");
+ }
+ }
+
void setClient(IMediaRoute2ProviderClient client) {
mClient = client;
publishState();
@@ -118,6 +152,23 @@
}
}
+ /**
+ * Route selection information.
+ *
+ * @see #notifyRouteSelected
+ */
+ public final class SelectToken {
+ final String mPackageName;
+ final String mRouteId;
+ final int mSeq;
+
+ SelectToken(String packageName, String routeId, int seq) {
+ mPackageName = packageName;
+ mRouteId = routeId;
+ mSeq = seq;
+ }
+ }
+
final class ProviderStub extends IMediaRoute2Provider.Stub {
ProviderStub() { }
@@ -129,10 +180,10 @@
@Override
public void requestSelectRoute(String packageName, String id, int seq) {
- // TODO: When introducing MediaRoute2ProviderService#sendConnectionHints(),
- // use the sequence number here properly.
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelectRoute,
- MediaRoute2ProviderService.this, packageName, id));
+ MediaRoute2ProviderService.this, packageName, id,
+ new SelectToken(packageName, id, seq)));
+
}
@Override
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 9cb7869..7b15d95 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -1318,6 +1318,7 @@
sStatic.rebindAsUser(userId);
}
+ //TODO: remove this and Client1Record in MediaRouter2ServiceImpl.
/**
* Sets the control categories of the application.
* Routes that support at least one of the given control categories only exists and are handled
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 94ac77a..35cb066 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -57,7 +57,8 @@
@IntDef(value = {
SELECT_REASON_UNKNOWN,
SELECT_REASON_USER_SELECTED,
- SELECT_REASON_FALLBACK})
+ SELECT_REASON_FALLBACK,
+ SELECT_REASON_SYSTEM_SELECTED})
public @interface SelectReason {}
/**
@@ -80,6 +81,13 @@
*/
public static final int SELECT_REASON_FALLBACK = 2;
+ /**
+ * This is passed from {@link com.android.server.media.MediaRouterService} when the route
+ * is selected in response to a request from other apps (e.g. System UI).
+ * @hide
+ */
+ public static final int SELECT_REASON_SYSTEM_SELECTED = 3;
+
private static final String TAG = "MR2";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final Object sLock = new Object();
@@ -485,6 +493,9 @@
}
mSelectingRoute = null;
}
+ if (reason == SELECT_REASON_SYSTEM_SELECTED) {
+ reason = SELECT_REASON_USER_SELECTED;
+ }
mSelectedRoute = route;
notifyRouteSelected(route, reason, controlHints);
}
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
index 378baf4..b65039d3 100644
--- a/media/jni/android_media_MediaDataSource.h
+++ b/media/jni/android_media_MediaDataSource.h
@@ -19,7 +19,7 @@
#include "jni.h"
-#include <media/IDataSource.h>
+#include <android/IDataSource.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
index b7cbd97..4fd5153 100644
--- a/media/jni/android_media_Streams.cpp
+++ b/media/jni/android_media_Streams.cpp
@@ -28,67 +28,6 @@
namespace android {
-AssetStream::AssetStream(SkStream* stream)
- : mStream(stream), mPosition(0) {
-}
-
-AssetStream::~AssetStream() {
-}
-
-piex::Error AssetStream::GetData(
- const size_t offset, const size_t length, std::uint8_t* data) {
- // Seek first.
- if (mPosition != offset) {
- if (!mStream->seek(offset)) {
- return piex::Error::kFail;
- }
- }
-
- // Read bytes.
- size_t size = mStream->read((void*)data, length);
- mPosition = offset + size;
-
- return size == length ? piex::Error::kOk : piex::Error::kFail;
-}
-
-BufferedStream::BufferedStream(SkStream* stream)
- : mStream(stream) {
-}
-
-BufferedStream::~BufferedStream() {
-}
-
-piex::Error BufferedStream::GetData(
- const size_t offset, const size_t length, std::uint8_t* data) {
- // Seek first.
- if (offset + length > mStreamBuffer.bytesWritten()) {
- size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten();
- if (sizeToRead <= kMinSizeToRead) {
- sizeToRead = kMinSizeToRead;
- }
-
- void* tempBuffer = malloc(sizeToRead);
- if (tempBuffer == NULL) {
- return piex::Error::kFail;
- }
-
- size_t bytesRead = mStream->read(tempBuffer, sizeToRead);
- if (bytesRead != sizeToRead) {
- free(tempBuffer);
- return piex::Error::kFail;
- }
- mStreamBuffer.write(tempBuffer, bytesRead);
- free(tempBuffer);
- }
-
- // Read bytes.
- if (mStreamBuffer.read((void*)data, offset, length)) {
- return piex::Error::kOk;
- } else {
- return piex::Error::kFail;
- }
-}
-
FileStream::FileStream(const int fd)
: mPosition(0) {
mFile = fdopen(fd, "r");
diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h
index d174f9a..800591c 100644
--- a/media/jni/android_media_Streams.h
+++ b/media/jni/android_media_Streams.h
@@ -25,53 +25,9 @@
#include <utils/KeyedVector.h>
#include <utils/String8.h>
#include <utils/StrongPointer.h>
-#include <SkStream.h>
-
namespace android {
-class AssetStream : public piex::StreamInterface {
-private:
- SkStream *mStream;
- size_t mPosition;
-
-public:
- explicit AssetStream(SkStream* stream);
- ~AssetStream();
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
-class BufferedStream : public piex::StreamInterface {
-private:
- SkStream *mStream;
- // Growable memory stream
- SkDynamicMemoryWStream mStreamBuffer;
-
- // Minimum size to read on filling the buffer.
- const size_t kMinSizeToRead = 8192;
-
-public:
- explicit BufferedStream(SkStream* stream);
- ~BufferedStream();
-
- // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer
- // provided by the caller, guaranteed to be at least "length" bytes long.
- // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at
- // 'offset' bytes from the start of the stream.
- // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not
- // change the contents of 'data'.
- piex::Error GetData(
- const size_t offset, const size_t length, std::uint8_t* data) override;
-};
-
class FileStream : public piex::StreamInterface {
private:
FILE *mFile;
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 f4f8d0b..6650f96 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -20,6 +20,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.os.Bundle;
import android.os.IBinder;
import java.util.HashMap;
@@ -95,7 +96,7 @@
}
@Override
- public void onSelectRoute(String packageName, String routeId) {
+ public void onSelectRoute(String packageName, String routeId, SelectToken token) {
MediaRoute2Info route = mRoutes.get(routeId);
if (route == null) {
return;
@@ -104,6 +105,7 @@
.setClientPackageName(packageName)
.build());
publishRoutes();
+ notifyRouteSelected(token, Bundle.EMPTY);
}
@Override
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index d0f7c78..c70ad8d 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -23,23 +23,19 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2;
import android.media.MediaRouter2Manager;
+import android.os.Bundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
-import org.junit.Assert;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,9 +91,14 @@
private Executor mExecutor;
private String mPackageName;
+ private final List<MediaRouter2Manager.Callback> mManagerCallbacks = new ArrayList<>();
+ private final List<MediaRouter2.Callback> mRouterCallbacks = new ArrayList<>();
+ private Map<String, MediaRoute2Info> mRoutes;
+
private static final List<String> CATEGORIES_ALL = new ArrayList();
private static final List<String> CATEGORIES_SPECIAL = new ArrayList();
private static final List<String> CATEGORIES_LIVE_AUDIO = new ArrayList<>();
+
static {
CATEGORIES_ALL.add(CATEGORY_SAMPLE);
CATEGORIES_ALL.add(CATEGORY_SPECIAL);
@@ -108,6 +109,7 @@
CATEGORIES_LIVE_AUDIO.add(CATEGORY_LIVE_AUDIO);
}
+
@Before
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
@@ -116,6 +118,16 @@
//TODO: If we need to support thread pool executors, change this to thread pool executor.
mExecutor = Executors.newSingleThreadExecutor();
mPackageName = mContext.getPackageName();
+
+ // ensure media router 2 client
+ addRouterCallback(new MediaRouter2.Callback());
+ mRoutes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
+ }
+
+ @After
+ public void tearDown() {
+ // unregister callbacks
+ clearCallbacks();
}
//TODO: Move to a separate file
@@ -132,10 +144,13 @@
assertNotEquals(routeInfo1, routeInfo3);
}
+ /**
+ * Tests if routes are added correctly when a new callback is registered.
+ */
@Test
public void testOnRoutesAdded() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
- MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
+ addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
assertTrue(routes.size() > 0);
@@ -145,27 +160,15 @@
}
}
}
- };
- mManager.registerCallback(mExecutor, callback);
+ });
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-
- mManager.unregisterCallback(callback);
}
@Test
public void testOnRoutesRemoved() throws Exception {
- MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
- mManager.registerCallback(mExecutor, mockCallback);
-
- MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
- mRouter2.registerCallback(mExecutor, routerCallback);
-
- Map<String, MediaRoute2Info> routes =
- waitAndGetRoutesWithManager(CATEGORIES_ALL);
-
CountDownLatch latch = new CountDownLatch(1);
- MediaRouter2Manager.Callback callback = new MediaRouter2Manager.Callback() {
+ addManagerCallback(new MediaRouter2Manager.Callback() {
@Override
public void onRoutesRemoved(List<MediaRoute2Info> routes) {
assertTrue(routes.size() > 0);
@@ -175,16 +178,12 @@
}
}
}
- };
- mManager.registerCallback(mExecutor, callback);
+ });
//TODO: Figure out a more proper way to test.
// (Control requests shouldn't be used in this way.)
- mRouter2.sendControlRequest(routes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE));
+ mRouter2.sendControlRequest(mRoutes.get(ROUTE_ID2), new Intent(ACTION_REMOVE_ROUTE));
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
-
- mRouter2.unregisterCallback(routerCallback);
- mManager.unregisterCallback(mockCallback);
}
/**
@@ -192,16 +191,10 @@
*/
@Test
public void testControlCategory() throws Exception {
- MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
- mManager.registerCallback(mExecutor, mockCallback);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
- Map<String, MediaRoute2Info> routes =
- waitAndGetRoutesWithManager(CATEGORIES_SPECIAL);
-
- Assert.assertEquals(1, routes.size());
- Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
-
- mManager.unregisterCallback(mockCallback);
+ assertEquals(1, routes.size());
+ assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
}
/**
@@ -209,37 +202,60 @@
*/
@Test
public void testGetRoutes() throws Exception {
- MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
- mRouter2.registerCallback(mExecutor, mockCallback);
-
Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_SPECIAL);
- Assert.assertEquals(1, routes.size());
- Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
-
- mRouter2.unregisterCallback(mockCallback);
+ assertEquals(1, routes.size());
+ assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
}
+ /**
+ * Tests if MR2.Callback.onRouteSelected is called when a route is selected from MR2Manager.
+ */
@Test
- public void testOnRouteSelected() throws Exception {
- MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
- MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
+ public void testRouterOnRouteSelected() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
- mManager.registerCallback(mExecutor, managerCallback);
- mRouter2.registerCallback(mExecutor, routerCallback);
+ addRouterCallback(new MediaRouter2.Callback() {
+ @Override
+ public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
+ if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
+ latch.countDown();
+ }
+ }
+ });
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
-
- MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
+ MediaRoute2Info routeToSelect = mRoutes.get(ROUTE_ID1);
assertNotNull(routeToSelect);
mManager.selectRoute(mPackageName, routeToSelect);
- verify(managerCallback, timeout(TIMEOUT_MS))
- .onRouteSelected(eq(mPackageName),
- argThat(route -> route != null && route.equals(routeToSelect)));
- mRouter2.unregisterCallback(routerCallback);
- mManager.unregisterCallback(managerCallback);
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+
+ /**
+ * Tests if MR2Manager.Callback.onRouteSelected is called
+ * when a route is selected by MR2Manager.
+ */
+ @Test
+ public void testManagerOnRouteSelected() throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ 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 = mRoutes.get(ROUTE_ID1);
+ assertNotNull(routeToSelect);
+
+ mManager.selectRoute(mPackageName, routeToSelect);
+
+ assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
/**
@@ -247,19 +263,13 @@
*/
@Test
public void testSingleProviderSelect() throws Exception {
- MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
-
- mRouter2.registerCallback(mExecutor, routerCallback);
-
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
-
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)),
+ () -> mManager.selectRoute(mPackageName, mRoutes.get(ROUTE_ID1)),
ROUTE_ID1,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
awaitOnRouteChangedManager(
- () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID2)),
+ () -> mManager.selectRoute(mPackageName, mRoutes.get(ROUTE_ID2)),
ROUTE_ID2,
route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
@@ -267,8 +277,6 @@
() -> mManager.unselectRoute(mPackageName),
ROUTE_ID2,
route -> TextUtils.equals(route.getClientPackageName(), null));
-
- mRouter2.unregisterCallback(routerCallback);
}
@Test
@@ -292,12 +300,7 @@
@Test
public void testControlVolumeWithManager() throws Exception {
- MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
-
- mRouter2.registerCallback(mExecutor, mockCallback);
- Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
-
- MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ MediaRoute2Info volRoute = mRoutes.get(ROUTE_ID_VARIABLE_VOLUME);
int originalVolume = volRoute.getVolume();
int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
@@ -310,24 +313,16 @@
() -> mManager.requestSetVolume(volRoute, originalVolume),
ROUTE_ID_VARIABLE_VOLUME,
(route -> route.getVolume() == originalVolume));
-
- mRouter2.unregisterCallback(mockCallback);
}
@Test
public void testVolumeHandling() throws Exception {
- MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
- mRouter2.registerCallback(mExecutor, mockCallback);
- Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CATEGORIES_ALL);
-
- MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
- MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ MediaRoute2Info fixedVolumeRoute = mRoutes.get(ROUTE_ID_FIXED_VOLUME);
+ MediaRoute2Info variableVolumeRoute = mRoutes.get(ROUTE_ID_VARIABLE_VOLUME);
assertEquals(PLAYBACK_VOLUME_FIXED, fixedVolumeRoute.getVolumeHandling());
assertEquals(PLAYBACK_VOLUME_VARIABLE, variableVolumeRoute.getVolumeHandling());
assertEquals(VOLUME_MAX, variableVolumeRoute.getVolumeMax());
-
- mRouter2.unregisterCallback(mockCallback);
}
@Test
@@ -368,6 +363,7 @@
latch.countDown();
}
}
+
@Override
public void onControlCategoriesChanged(String packageName) {
if (TextUtils.equals(mPackageName, packageName)) {
@@ -401,7 +397,7 @@
};
mRouter2.registerCallback(mExecutor, callback);
try {
- new Thread(task).start();
+ task.run();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mRouter2.unregisterCallback(callback);
@@ -422,7 +418,7 @@
};
mManager.registerCallback(mExecutor, callback);
try {
- new Thread(task).start();
+ task.run();
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} finally {
mManager.unregisterCallback(callback);
@@ -433,9 +429,31 @@
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
for (MediaRoute2Info route : routes) {
- // intentionally not route.getUniqueId() for convenience.
+ // intentionally not using route.getUniqueId() for convenience.
routeMap.put(route.getId(), route);
}
return routeMap;
}
+
+ private void addManagerCallback(MediaRouter2Manager.Callback callback) {
+ mManagerCallbacks.add(callback);
+ mManager.registerCallback(mExecutor, callback);
+ }
+
+ private void addRouterCallback(MediaRouter2.Callback callback) {
+ mRouterCallbacks.add(callback);
+ mRouter2.registerCallback(mExecutor, callback);
+ }
+
+ private void clearCallbacks() {
+ for (MediaRouter2Manager.Callback callback : mManagerCallbacks) {
+ mManager.unregisterCallback(callback);
+ }
+ mManagerCallbacks.clear();
+
+ for (MediaRouter2.Callback callback : mRouterCallbacks) {
+ mRouter2.unregisterCallback(callback);
+ }
+ mRouterCallbacks.clear();
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
index f90947c..f2bf811 100644
--- a/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
+++ b/packages/CarSystemUI/src/com/android/systemui/TEST_MAPPING
@@ -1,5 +1,5 @@
{
- "auto-postsubmit": [
+ "auto-end-to-end-postsubmit": [
{
"name": "AndroidAutoUiTests",
"options" : [
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 142078e..9e49826 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -32,9 +32,11 @@
import static android.os.image.DynamicSystemClient.STATUS_NOT_STARTED;
import static android.os.image.DynamicSystemClient.STATUS_READY;
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_CANCELLED;
import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_EXCEPTION;
-import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_INVALID_URL;
import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_IO;
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_FORMAT;
+import static com.android.dynsystem.InstallationAsyncTask.RESULT_ERROR_UNSUPPORTED_URL;
import static com.android.dynsystem.InstallationAsyncTask.RESULT_OK;
import android.app.Notification;
@@ -66,11 +68,10 @@
* cancel and confirm commnands.
*/
public class DynamicSystemInstallationService extends Service
- implements InstallationAsyncTask.InstallStatusListener {
+ implements InstallationAsyncTask.ProgressListener {
private static final String TAG = "DynSystemInstallationService";
-
// TODO (b/131866826): This is currently for test only. Will move this to System API.
static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
@@ -121,9 +122,12 @@
private DynamicSystemManager mDynSystem;
private NotificationManager mNM;
- private long mSystemSize;
- private long mUserdataSize;
- private long mInstalledSize;
+ private int mNumInstalledPartitions;
+
+ private String mCurrentPartitionName;
+ private long mCurrentPartitionSize;
+ private long mCurrentPartitionInstalledSize;
+
private boolean mJustCancelledByUser;
// This is for testing only now
@@ -176,8 +180,12 @@
}
@Override
- public void onProgressUpdate(long installedSize) {
- mInstalledSize = installedSize;
+ public void onProgressUpdate(InstallationAsyncTask.Progress progress) {
+ mCurrentPartitionName = progress.mPartitionName;
+ mCurrentPartitionSize = progress.mPartitionSize;
+ mCurrentPartitionInstalledSize = progress.mInstalledSize;
+ mNumInstalledPartitions = progress.mNumInstalledPartitions;
+
postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
}
@@ -197,11 +205,16 @@
resetTaskAndStop();
switch (result) {
+ case RESULT_CANCELLED:
+ postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
+ break;
+
case RESULT_ERROR_IO:
postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
break;
- case RESULT_ERROR_INVALID_URL:
+ case RESULT_ERROR_UNSUPPORTED_URL:
+ case RESULT_ERROR_UNSUPPORTED_FORMAT:
postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
break;
@@ -211,12 +224,6 @@
}
}
- @Override
- public void onCancelled() {
- resetTaskAndStop();
- postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
- }
-
private void executeInstallCommand(Intent intent) {
if (!verifyRequest(intent)) {
Log.e(TAG, "Verification failed. Did you use VerificationActivity?");
@@ -234,12 +241,13 @@
}
String url = intent.getDataString();
- mSystemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
- mUserdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
+ long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
+ long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
+ // TODO: better constructor or builder
mInstallTask = new InstallationAsyncTask(
- url, mSystemSize, mUserdataSize, this, mDynSystem, this);
+ url, systemSize, userdataSize, this, mDynSystem, this);
mInstallTask.execute();
@@ -257,7 +265,7 @@
mJustCancelledByUser = true;
if (mInstallTask.cancel(false)) {
- // Will cleanup and post status in onCancelled()
+ // Will cleanup and post status in onResult()
Log.d(TAG, "Cancel request filed successfully");
} else {
Log.e(TAG, "Trying to cancel installation while it's already completed.");
@@ -288,7 +296,7 @@
private void executeRebootToDynSystemCommand() {
boolean enabled = false;
- if (mInstallTask != null && mInstallTask.getResult() == RESULT_OK) {
+ if (mInstallTask != null && mInstallTask.isCompleted()) {
enabled = mInstallTask.commit();
} else if (isDynamicSystemInstalled()) {
enabled = mDynSystem.setEnable(true, true);
@@ -380,8 +388,16 @@
case STATUS_IN_PROGRESS:
builder.setContentText(getString(R.string.notification_install_inprogress));
- int max = (int) Math.max((mSystemSize + mUserdataSize) >> 20, 1);
- int progress = (int) (mInstalledSize >> 20);
+ int max = 1024;
+ int progress = 0;
+
+ int currentMax = max >> (mNumInstalledPartitions + 1);
+ progress = max - currentMax * 2;
+
+ long currentProgress = (mCurrentPartitionInstalledSize >> 20) * currentMax
+ / Math.max(mCurrentPartitionSize >> 20, 1);
+
+ progress += (int) currentProgress;
builder.setProgress(max, progress, false);
@@ -464,7 +480,8 @@
throws RemoteException {
Bundle bundle = new Bundle();
- bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mInstalledSize);
+ // TODO: send more info to the clients
+ bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mCurrentPartitionInstalledSize);
if (detail != null) {
bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
@@ -492,9 +509,7 @@
return STATUS_IN_PROGRESS;
case FINISHED:
- int result = mInstallTask.getResult();
-
- if (result == RESULT_OK) {
+ if (mInstallTask.isCompleted()) {
return STATUS_READY;
} else {
throw new IllegalStateException("A failed InstallationTask is not reset");
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 19ae970..b206a1f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -17,7 +17,6 @@
package com.android.dynsystem;
import android.content.Context;
-import android.gsi.GsiProgress;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.MemoryFile;
@@ -27,35 +26,70 @@
import android.webkit.URLUtil;
import java.io.BufferedInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
import java.util.Locale;
import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
-class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
+class InstallationAsyncTask extends AsyncTask<String, InstallationAsyncTask.Progress, Throwable> {
private static final String TAG = "InstallationAsyncTask";
private static final int READ_BUFFER_SIZE = 1 << 13;
+ private static final long MIN_PROGRESS_TO_PUBLISH = 1 << 27;
- private class InvalidImageUrlException extends RuntimeException {
- private InvalidImageUrlException(String message) {
+ private static final List<String> UNSUPPORTED_PARTITIONS =
+ Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
+
+ private class UnsupportedUrlException extends RuntimeException {
+ private UnsupportedUrlException(String message) {
super(message);
}
}
- /** Not completed, including being cancelled */
- static final int NO_RESULT = 0;
+ private class UnsupportedFormatException extends RuntimeException {
+ private UnsupportedFormatException(String message) {
+ super(message);
+ }
+ }
+
+ /** UNSET means the installation is not completed */
+ static final int RESULT_UNSET = 0;
static final int RESULT_OK = 1;
- static final int RESULT_ERROR_IO = 2;
- static final int RESULT_ERROR_INVALID_URL = 3;
+ static final int RESULT_CANCELLED = 2;
+ static final int RESULT_ERROR_IO = 3;
+ static final int RESULT_ERROR_UNSUPPORTED_URL = 4;
+ static final int RESULT_ERROR_UNSUPPORTED_FORMAT = 5;
static final int RESULT_ERROR_EXCEPTION = 6;
- interface InstallStatusListener {
- void onProgressUpdate(long installedSize);
+ class Progress {
+ String mPartitionName;
+ long mPartitionSize;
+ long mInstalledSize;
+
+ int mNumInstalledPartitions;
+
+ Progress(String partitionName, long partitionSize, long installedSize,
+ int numInstalled) {
+ mPartitionName = partitionName;
+ mPartitionSize = partitionSize;
+ mInstalledSize = installedSize;
+
+ mNumInstalledPartitions = numInstalled;
+ }
+ }
+
+ interface ProgressListener {
+ void onProgressUpdate(Progress progress);
void onResult(int resultCode, Throwable detail);
- void onCancelled();
}
private final String mUrl;
@@ -63,16 +97,17 @@
private final long mUserdataSize;
private final Context mContext;
private final DynamicSystemManager mDynSystem;
- private final InstallStatusListener mListener;
+ private final ProgressListener mListener;
private DynamicSystemManager.Session mInstallationSession;
- private int mResult = NO_RESULT;
+ private boolean mIsZip;
+ private boolean mIsCompleted;
private InputStream mStream;
-
+ private ZipFile mZipFile;
InstallationAsyncTask(String url, long systemSize, long userdataSize, Context context,
- DynamicSystemManager dynSystem, InstallStatusListener listener) {
+ DynamicSystemManager dynSystem, ProgressListener listener) {
mUrl = url;
mSystemSize = systemSize;
mUserdataSize = userdataSize;
@@ -82,133 +117,292 @@
}
@Override
- protected void onPreExecute() {
- mListener.onProgressUpdate(0);
- }
-
- @Override
protected Throwable doInBackground(String... voids) {
Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
try {
- long installedSize = 0;
- long reportedInstalledSize = 0;
+ // call DynamicSystemManager to cleanup stuff
+ mDynSystem.remove();
- long minStepToReport = (mSystemSize + mUserdataSize) / 100;
+ verifyAndPrepare();
- // init input stream before calling startInstallation(), which takes 90 seconds.
- initInputStream();
+ mDynSystem.startInstallation();
- Thread thread =
- new Thread(
- () -> {
- mDynSystem.startInstallation();
- mDynSystem.createPartition("userdata", mUserdataSize, false);
- mInstallationSession =
- mDynSystem.createPartition("system", mSystemSize, true);
- });
-
- thread.start();
-
- while (thread.isAlive()) {
- if (isCancelled()) {
- boolean aborted = mDynSystem.abort();
- Log.d(TAG, "Called DynamicSystemManager.abort(), result = " + aborted);
- return null;
- }
-
- GsiProgress progress = mDynSystem.getInstallationProgress();
- installedSize = progress.bytes_processed;
-
- if (installedSize > reportedInstalledSize + minStepToReport) {
- publishProgress(installedSize);
- reportedInstalledSize = installedSize;
- }
-
- Thread.sleep(10);
+ installUserdata();
+ if (isCancelled()) {
+ mDynSystem.remove();
+ return null;
}
- if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: "
- + (mSystemSize + mUserdataSize));
+ installImages();
+ if (isCancelled()) {
+ mDynSystem.remove();
+ return null;
}
- installedSize = mUserdataSize;
-
- MemoryFile memoryFile = new MemoryFile("dsu", READ_BUFFER_SIZE);
- byte[] bytes = new byte[READ_BUFFER_SIZE];
- mInstallationSession.setAshmem(
- new ParcelFileDescriptor(memoryFile.getFileDescriptor()), READ_BUFFER_SIZE);
- int numBytesRead;
- Log.d(TAG, "Start installation loop");
- while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
- memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
- if (isCancelled()) {
- break;
- }
- if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
- throw new IOException("Failed write() to DynamicSystem");
- }
-
- installedSize += numBytesRead;
-
- if (installedSize > reportedInstalledSize + minStepToReport) {
- publishProgress(installedSize);
- reportedInstalledSize = installedSize;
- }
- }
mDynSystem.finishInstallation();
- return null;
-
} catch (Exception e) {
e.printStackTrace();
+ mDynSystem.remove();
return e;
} finally {
close();
}
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Throwable detail) {
+ int result = RESULT_UNSET;
+
+ if (detail == null) {
+ result = RESULT_OK;
+ mIsCompleted = true;
+ } else if (detail instanceof IOException) {
+ result = RESULT_ERROR_IO;
+ } else if (detail instanceof UnsupportedUrlException) {
+ result = RESULT_ERROR_UNSUPPORTED_URL;
+ } else if (detail instanceof UnsupportedFormatException) {
+ result = RESULT_ERROR_UNSUPPORTED_FORMAT;
+ } else {
+ result = RESULT_ERROR_EXCEPTION;
+ }
+
+ Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+
+ mListener.onResult(result, detail);
}
@Override
protected void onCancelled() {
Log.d(TAG, "onCancelled(), URL: " + mUrl);
- mListener.onCancelled();
- }
-
- @Override
- protected void onPostExecute(Throwable detail) {
- if (detail == null) {
- mResult = RESULT_OK;
- } else if (detail instanceof IOException) {
- mResult = RESULT_ERROR_IO;
- } else if (detail instanceof InvalidImageUrlException) {
- mResult = RESULT_ERROR_INVALID_URL;
+ if (mDynSystem.abort()) {
+ Log.d(TAG, "Installation aborted");
} else {
- mResult = RESULT_ERROR_EXCEPTION;
+ Log.w(TAG, "DynamicSystemManager.abort() returned false");
}
- Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + mResult);
-
- mListener.onResult(mResult, detail);
+ mListener.onResult(RESULT_CANCELLED, null);
}
@Override
- protected void onProgressUpdate(Long... values) {
- long progress = values[0];
+ protected void onProgressUpdate(Progress... values) {
+ Progress progress = values[0];
mListener.onProgressUpdate(progress);
}
- private void initInputStream() throws IOException, InvalidImageUrlException {
- if (URLUtil.isNetworkUrl(mUrl) || URLUtil.isFileUrl(mUrl)) {
- mStream = new BufferedInputStream(new GZIPInputStream(new URL(mUrl).openStream()));
- } else if (URLUtil.isContentUrl(mUrl)) {
- Uri uri = Uri.parse(mUrl);
- mStream = new BufferedInputStream(new GZIPInputStream(
- mContext.getContentResolver().openInputStream(uri)));
+ private void verifyAndPrepare() throws Exception {
+ String extension = mUrl.substring(mUrl.lastIndexOf('.') + 1);
+
+ if ("gz".equals(extension) || "gzip".equals(extension)) {
+ mIsZip = false;
+ } else if ("zip".equals(extension)) {
+ mIsZip = true;
} else {
- throw new InvalidImageUrlException(
- String.format(Locale.US, "Unsupported file source: %s", mUrl));
+ throw new UnsupportedFormatException(
+ String.format(Locale.US, "Unsupported file format: %s", mUrl));
+ }
+
+ if (URLUtil.isNetworkUrl(mUrl)) {
+ mStream = new URL(mUrl).openStream();
+ } else if (URLUtil.isFileUrl(mUrl)) {
+ if (mIsZip) {
+ mZipFile = new ZipFile(new File(new URL(mUrl).toURI()));
+ } else {
+ mStream = new URL(mUrl).openStream();
+ }
+ } else if (URLUtil.isContentUrl(mUrl)) {
+ mStream = mContext.getContentResolver().openInputStream(Uri.parse(mUrl));
+ } else {
+ throw new UnsupportedUrlException(
+ String.format(Locale.US, "Unsupported URL: %s", mUrl));
+ }
+ }
+
+ private void installUserdata() throws Exception {
+ Thread thread = new Thread(() -> {
+ mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
+ });
+
+ Log.d(TAG, "Creating partition: userdata");
+ thread.start();
+
+ long installedSize = 0;
+ Progress progress = new Progress("userdata", mUserdataSize, installedSize, 0);
+
+ while (thread.isAlive()) {
+ if (isCancelled()) {
+ return;
+ }
+
+ installedSize = mDynSystem.getInstallationProgress().bytes_processed;
+
+ if (installedSize > progress.mInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+ progress.mInstalledSize = installedSize;
+ publishProgress(progress);
+ }
+
+ Thread.sleep(10);
+ }
+
+ if (mInstallationSession == null) {
+ throw new IOException(
+ "Failed to start installation with requested size: " + mUserdataSize);
+ }
+ }
+
+ private void installImages() throws IOException, InterruptedException {
+ if (mStream != null) {
+ if (mIsZip) {
+ installStreamingZipUpdate();
+ } else {
+ installStreamingGzUpdate();
+ }
+ } else {
+ installLocalZipUpdate();
+ }
+ }
+
+ private void installStreamingGzUpdate() throws IOException, InterruptedException {
+ Log.d(TAG, "To install a streaming GZ update");
+ installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
+ }
+
+ private void installStreamingZipUpdate() throws IOException, InterruptedException {
+ Log.d(TAG, "To install a streaming ZIP update");
+
+ ZipInputStream zis = new ZipInputStream(mStream);
+ ZipEntry zipEntry = null;
+
+ int numInstalledPartitions = 1;
+
+ while ((zipEntry = zis.getNextEntry()) != null) {
+ if (installImageFromAnEntry(zipEntry, zis, numInstalledPartitions)) {
+ numInstalledPartitions++;
+ }
+
+ if (isCancelled()) {
+ break;
+ }
+ }
+ }
+
+ private void installLocalZipUpdate() throws IOException, InterruptedException {
+ Log.d(TAG, "To install a local ZIP update");
+
+ Enumeration<? extends ZipEntry> entries = mZipFile.entries();
+ int numInstalledPartitions = 1;
+
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (installImageFromAnEntry(
+ entry, mZipFile.getInputStream(entry), numInstalledPartitions)) {
+ numInstalledPartitions++;
+ }
+
+ if (isCancelled()) {
+ break;
+ }
+ }
+ }
+
+ private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
+ int numInstalledPartitions) throws IOException, InterruptedException {
+ String name = entry.getName();
+
+ Log.d(TAG, "ZipEntry: " + name);
+
+ if (!name.endsWith(".img")) {
+ return false;
+ }
+
+ String partitionName = name.substring(0, name.length() - 4);
+
+ if (UNSUPPORTED_PARTITIONS.contains(partitionName)) {
+ Log.d(TAG, name + " installation is not supported, skip it.");
+ return false;
+ }
+
+ long uncompressedSize = entry.getSize();
+
+ installImage(partitionName, uncompressedSize, is, numInstalledPartitions);
+
+ return true;
+ }
+
+ private void installImage(String partitionName, long uncompressedSize, InputStream is,
+ int numInstalledPartitions) throws IOException, InterruptedException {
+
+ SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
+
+ long unsparseSize = sis.getUnsparseSize();
+
+ final long partitionSize;
+
+ if (unsparseSize != -1) {
+ partitionSize = unsparseSize;
+ Log.d(TAG, partitionName + " is sparse, raw size = " + unsparseSize);
+ } else if (uncompressedSize != -1) {
+ partitionSize = uncompressedSize;
+ Log.d(TAG, partitionName + " is already unsparse, raw size = " + uncompressedSize);
+ } else {
+ throw new IOException("Cannot get raw size for " + partitionName);
+ }
+
+ Thread thread = new Thread(() -> {
+ mInstallationSession =
+ mDynSystem.createPartition(partitionName, partitionSize, true);
+ });
+
+ Log.d(TAG, "Start creating partition: " + partitionName);
+ thread.start();
+
+ while (thread.isAlive()) {
+ if (isCancelled()) {
+ return;
+ }
+
+ Thread.sleep(10);
+ }
+
+ if (mInstallationSession == null) {
+ throw new IOException(
+ "Failed to start installation with requested size: " + partitionSize);
+ }
+
+ Log.d(TAG, "Start installing: " + partitionName);
+
+ MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, READ_BUFFER_SIZE);
+ ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor());
+
+ mInstallationSession.setAshmem(pfd, READ_BUFFER_SIZE);
+
+ long installedSize = 0;
+ Progress progress = new Progress(
+ partitionName, partitionSize, installedSize, numInstalledPartitions);
+
+ byte[] bytes = new byte[READ_BUFFER_SIZE];
+ int numBytesRead;
+
+ while ((numBytesRead = sis.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+ if (isCancelled()) {
+ return;
+ }
+
+ memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
+
+ if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
+ throw new IOException("Failed write() to DynamicSystem");
+ }
+
+ installedSize += numBytesRead;
+
+ if (installedSize > progress.mInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+ progress.mInstalledSize = installedSize;
+ publishProgress(progress);
+ }
}
}
@@ -218,20 +412,20 @@
mStream.close();
mStream = null;
}
+ if (mZipFile != null) {
+ mZipFile.close();
+ mZipFile = null;
+ }
} catch (IOException e) {
// ignore
}
}
- int getResult() {
- return mResult;
+ boolean isCompleted() {
+ return mIsCompleted;
}
boolean commit() {
- if (mInstallationSession == null) {
- return false;
- }
-
- return mInstallationSession.commit();
+ return mDynSystem.setEnable(true, true);
}
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
new file mode 100644
index 0000000..72230b4
--- /dev/null
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/SparseInputStream.java
@@ -0,0 +1,199 @@
+/*
+ * 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.dynsystem;
+
+import static java.lang.Math.min;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * SparseInputStream read from upstream and detects the data format. If the upstream is a valid
+ * sparse data, it will unsparse it on the fly. Otherwise, it just passthrough as is.
+ */
+public class SparseInputStream extends InputStream {
+ static final int FILE_HDR_SIZE = 28;
+ static final int CHUNK_HDR_SIZE = 12;
+
+ /**
+ * This class represents a chunk in the Android sparse image.
+ *
+ * @see system/core/libsparse/sparse_format.h
+ */
+ private class SparseChunk {
+ static final short RAW = (short) 0xCAC1;
+ static final short FILL = (short) 0xCAC2;
+ static final short DONTCARE = (short) 0xCAC3;
+ public short mChunkType;
+ public int mChunkSize;
+ public int mTotalSize;
+ public byte[] fill;
+ public String toString() {
+ return String.format(
+ "type: %x, chunk_size: %d, total_size: %d", mChunkType, mChunkSize, mTotalSize);
+ }
+ }
+
+ private byte[] readFull(InputStream in, int size) throws IOException {
+ byte[] buf = new byte[size];
+ for (int done = 0, n = 0; done < size; done += n) {
+ if ((n = in.read(buf, done, size - done)) < 0) {
+ throw new IOException("Failed to readFull");
+ }
+ }
+ return buf;
+ }
+
+ private ByteBuffer readBuffer(InputStream in, int size) throws IOException {
+ return ByteBuffer.wrap(readFull(in, size)).order(ByteOrder.LITTLE_ENDIAN);
+ }
+
+ private SparseChunk readChunk(InputStream in) throws IOException {
+ SparseChunk chunk = new SparseChunk();
+ ByteBuffer buf = readBuffer(in, CHUNK_HDR_SIZE);
+ chunk.mChunkType = buf.getShort();
+ buf.getShort();
+ chunk.mChunkSize = buf.getInt();
+ chunk.mTotalSize = buf.getInt();
+ return chunk;
+ }
+
+ private BufferedInputStream mIn;
+ private boolean mIsSparse;
+ private long mBlockSize;
+ private long mTotalBlocks;
+ private long mTotalChunks;
+ private SparseChunk mCur;
+ private long mLeft;
+ private int mCurChunks;
+
+ public SparseInputStream(BufferedInputStream in) throws IOException {
+ mIn = in;
+ in.mark(FILE_HDR_SIZE * 2);
+ ByteBuffer buf = readBuffer(mIn, FILE_HDR_SIZE);
+ mIsSparse = (buf.getInt() == 0xed26ff3a);
+ if (!mIsSparse) {
+ mIn.reset();
+ return;
+ }
+ int major = buf.getShort();
+ int minor = buf.getShort();
+
+ if (major > 0x1 || minor > 0x0) {
+ throw new IOException("Unsupported sparse version: " + major + "." + minor);
+ }
+
+ if (buf.getShort() != FILE_HDR_SIZE) {
+ throw new IOException("Illegal file header size");
+ }
+ if (buf.getShort() != CHUNK_HDR_SIZE) {
+ throw new IOException("Illegal chunk header size");
+ }
+ mBlockSize = buf.getInt();
+ if ((mBlockSize & 0x3) != 0) {
+ throw new IOException("Illegal block size, must be a multiple of 4");
+ }
+ mTotalBlocks = buf.getInt();
+ mTotalChunks = buf.getInt();
+ mLeft = mCurChunks = 0;
+ }
+
+ /**
+ * Check if it needs to open a new chunk.
+ *
+ * @return true if it's EOF
+ */
+ private boolean prepareChunk() throws IOException {
+ if (mCur == null || mLeft <= 0) {
+ if (++mCurChunks > mTotalChunks) return true;
+ mCur = readChunk(mIn);
+ if (mCur.mChunkType == SparseChunk.FILL) {
+ mCur.fill = readFull(mIn, 4);
+ }
+ mLeft = mCur.mChunkSize * mBlockSize;
+ }
+ return mLeft == 0;
+ }
+
+ /**
+ * It overrides the InputStream.read(byte[] buf)
+ */
+ public int read(byte[] buf) throws IOException {
+ if (!mIsSparse) {
+ return mIn.read(buf);
+ }
+ if (prepareChunk()) return -1;
+ int n = -1;
+ switch (mCur.mChunkType) {
+ case SparseChunk.RAW:
+ n = mIn.read(buf, 0, (int) min(mLeft, buf.length));
+ mLeft -= n;
+ return n;
+ case SparseChunk.DONTCARE:
+ n = (int) min(mLeft, buf.length);
+ Arrays.fill(buf, 0, n - 1, (byte) 0);
+ mLeft -= n;
+ return n;
+ case SparseChunk.FILL:
+ // The FILL type is rarely used, so use a simple implmentation.
+ return super.read(buf);
+ default:
+ throw new IOException("Unsupported Chunk:" + mCur.toString());
+ }
+ }
+
+ /**
+ * It overrides the InputStream.read()
+ */
+ public int read() throws IOException {
+ if (!mIsSparse) {
+ return mIn.read();
+ }
+ if (prepareChunk()) return -1;
+ int ret = -1;
+ switch (mCur.mChunkType) {
+ case SparseChunk.RAW:
+ ret = mIn.read();
+ break;
+ case SparseChunk.DONTCARE:
+ ret = 0;
+ break;
+ case SparseChunk.FILL:
+ ret = mCur.fill[(4 - ((int) mLeft & 0x3)) & 0x3];
+ break;
+ default:
+ throw new IOException("Unsupported Chunk:" + mCur.toString());
+ }
+ mLeft--;
+ return ret;
+ }
+
+ /**
+ * Get the unsparse size
+ * @return -1 if unknown
+ */
+ public long getUnsparseSize() {
+ if (!mIsSparse) {
+ return -1;
+ }
+ return mBlockSize * mTotalBlocks;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
index 1c62879..0a1a122 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/VisibilityLoggerMixin.java
@@ -76,6 +76,19 @@
}
/**
+ * Logs the elapsed time from onAttach to calling {@link #writeElapsedTimeMetric(int, String)}.
+ * @param action : The value of the Action Enums.
+ * @param key : The value of special key string.
+ */
+ public void writeElapsedTimeMetric(int action, String key) {
+ if (mMetricsFeature == null || mMetricsCategory == METRICS_CATEGORY_UNKNOWN) {
+ return;
+ }
+ final int elapse = (int) (SystemClock.elapsedRealtime() - mTimestamp);
+ mMetricsFeature.action(METRICS_CATEGORY_UNKNOWN, action, mMetricsCategory, key, elapse);
+ }
+
+ /**
* Sets source metrics category for this logger. Source is the caller that opened this UI.
*/
public void setSourceMetricsCategory(Activity activity) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index f14def1..653c8ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -328,7 +328,6 @@
boolean enabled, int rowId, Uri conditionId) {
if (tag.lines == null) {
tag.lines = row.findViewById(android.R.id.content);
- tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
if (tag.line1 == null) {
tag.line1 = (TextView) row.findViewById(android.R.id.text1);
@@ -364,6 +363,7 @@
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, false /*down*/, rowId);
+ tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
});
@@ -373,6 +373,7 @@
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, true /*up*/, rowId);
+ tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
});
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
index 66ee802..01a5789 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java
@@ -234,6 +234,7 @@
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, false /*down*/, rowIndex);
+ tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
});
@@ -243,6 +244,7 @@
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, true /*up*/, rowIndex);
+ tag.lines.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
}
});
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f2af40e..b0bdf1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -33,7 +33,6 @@
import android.net.NetworkScoreManager;
import android.net.NetworkScorerAppData;
import android.net.ScoredNetwork;
-import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiConfiguration.KeyMgmt;
@@ -47,7 +46,6 @@
import android.os.Bundle;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1612,13 +1610,8 @@
final ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (state == DetailedState.CONNECTED) {
- IWifiManager wifiManager = IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
- NetworkCapabilities nc = null;
-
- try {
- nc = cm.getNetworkCapabilities(wifiManager.getCurrentNetwork());
- } catch (RemoteException e) {}
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
+ NetworkCapabilities nc = cm.getNetworkCapabilities(wifiManager.getCurrentNetwork());
if (nc != null) {
if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 7d06e6c..7b1c382 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -120,7 +120,7 @@
private OsuProvider createOsuProvider() {
Map<String, String> friendlyNames = new HashMap<>();
friendlyNames.put("en", OSU_FRIENDLY_NAME);
- return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null, null);
+ return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null);
}
@Before
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 94fbc54..81cf118 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -336,7 +336,7 @@
private static OsuProvider buildOsuProvider(String friendlyName) {
Map<String, String> friendlyNames = new HashMap<>();
friendlyNames.put("en", friendlyName);
- return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null, null);
+ return new OsuProvider((WifiSsid) null, friendlyNames, null, null, null, null);
}
private WifiTracker createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 05246a4..21b3ba3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -39,7 +39,6 @@
<bool name="def_bluetooth_on">true</bool>
<bool name="def_wifi_display_on">false</bool>
<bool name="def_install_non_market_apps">false</bool>
- <bool name="def_package_verifier_enable">true</bool>
<!-- 0 == off, 3 == on -->
<integer name="def_location_mode">3</integer>
<bool name="assisted_gps_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java
new file mode 100644
index 0000000..5f3d7f1
--- /dev/null
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/AccessibilityShortcutTargetListValidator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+import android.text.TextUtils;
+
+/**
+ * Ensure a restored value is a string in the format the accessibility shortcut system handles
+ *
+ * @hide
+ */
+public final class AccessibilityShortcutTargetListValidator extends ListValidator {
+ public AccessibilityShortcutTargetListValidator() {
+ super(":");
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return !TextUtils.isEmpty(entry);
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ if (TextUtils.isEmpty(item)) {
+ return false;
+ }
+ return (COMPONENT_NAME_VALIDATOR.validate(item) || PACKAGE_NAME_VALIDATOR.validate(item));
+ }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java b/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
index a6001d2..104fa11 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/ListValidator.java
@@ -35,7 +35,7 @@
if (!isEntryValid(value)) {
return false;
}
- String[] items = value.split(",");
+ String[] items = value.split(mListSplitRegex);
for (String item : items) {
if (!isItemValid(item)) {
return false;
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 9460d27f..090af98 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -16,6 +16,7 @@
package android.provider.settings.validators;
+import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
@@ -71,10 +72,13 @@
VALIDATORS.put(Secure.TOUCH_EXPLORATION_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
- Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
+ Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
// technically either ComponentName or class name, but there's proper value
// validation at callsites, so allow any non-null string
- VALIDATORS.put(Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, value -> value != null);
+ VALIDATORS.put(
+ Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 224042c..71c7544 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -204,4 +204,7 @@
new InclusiveIntegerRangeValidator(0, 100);
static final Validator VIBRATION_INTENSITY_VALIDATOR = new InclusiveIntegerRangeValidator(0, 3);
+
+ static final Validator ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR =
+ new AccessibilityShortcutTargetListValidator();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0c4db49..2027345 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -1191,19 +1191,7 @@
}
if (upgradeVersion == 81) {
- // Add package verification setting
- db.beginTransaction();
- SQLiteStatement stmt = null;
- try {
- stmt = db.compileStatement("INSERT OR REPLACE INTO secure(name,value)"
- + " VALUES(?,?);");
- loadBooleanSetting(stmt, Settings.Global.PACKAGE_VERIFIER_ENABLE,
- R.bool.def_package_verifier_enable);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- if (stmt != null) stmt.close();
- }
+ // package_verifier_enable has been removed
upgradeVersion = 82;
}
@@ -1305,7 +1293,6 @@
db.beginTransaction();
try {
String[] settingsToMove = {
- Settings.Global.PACKAGE_VERIFIER_ENABLE,
Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE
};
@@ -2470,9 +2457,6 @@
loadDefaultAnimationSettings(stmt);
// --- Previously in 'secure'
- loadBooleanSetting(stmt, Settings.Global.PACKAGE_VERIFIER_ENABLE,
- R.bool.def_package_verifier_enable);
-
loadBooleanSetting(stmt, Settings.Global.WIFI_ON,
R.bool.def_wifi_on);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 33e0fd2..0a2dd38 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1162,9 +1162,6 @@
final long pkgVerifierToken = p.start(GlobalSettingsProto.PACKAGE_VERIFIER);
dumpSetting(s, p,
- Settings.Global.PACKAGE_VERIFIER_ENABLE,
- GlobalSettingsProto.PackageVerifier.ENABLED);
- dumpSetting(s, p,
Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
GlobalSettingsProto.PackageVerifier.TIMEOUT);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 0959de9..93a1407 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -967,11 +967,6 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- Setting enable = getGlobalSetting(
- Settings.Global.PACKAGE_VERIFIER_ENABLE);
- String enableValue = enable != null ? enable.getValue() : null;
- updateGlobalSetting(Settings.Global.PACKAGE_VERIFIER_ENABLE,
- enableValue, null, true, userId, true);
Setting include = getGlobalSetting(
Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB);
String includeValue = include != null ? include.getValue() : null;
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index eefba5d..540726b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -387,7 +387,6 @@
Settings.Global.OVERLAY_DISPLAY_DEVICES,
Settings.Global.PAC_CHANGE_DELAY,
Settings.Global.PACKAGE_VERIFIER_DEFAULT_RESPONSE,
- Settings.Global.PACKAGE_VERIFIER_ENABLE,
Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB,
Settings.Global.PACKAGE_VERIFIER_SETTING_VISIBLE,
Settings.Global.PACKAGE_VERIFIER_TIMEOUT,
diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
index a3b0835..bb9e6f6 100644
--- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -297,6 +297,18 @@
}
@Test
+ public void testAccessibilityShortcutTargetValidator() {
+ assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+ "com.google.android/.AccessibilityShortcutTarget"));
+ assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+ "com.google.android"));
+ assertTrue(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+ "com.google.android/.AccessibilityShortcutTarget:com.google.android"));
+ assertFalse(SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR.validate(
+ "com.google.android/.AccessibilityShortcutTarget:com.google.@android"));
+ }
+
+ @Test
public void ensureAllBackedUpGlobalSettingsHaveValidators() {
String offenders = getOffenders(concat(GlobalSettings.SETTINGS_TO_BACKUP,
Settings.Global.LEGACY_RESTORE_SETTINGS), GlobalSettingsValidators.VALIDATORS);
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 1bfc4c0..b90a371 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -24,7 +24,25 @@
android:outlineProvider="none"
android:elevation="5dp" > <!-- Put it above the status bar header -->
- <include layout="@layout/keyguard_indication_area_overlay" />
+ <LinearLayout
+ android:id="@+id/keyguard_indication_area"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
+ android:layout_gravity="bottom|center_horizontal"
+ android:orientation="vertical">
+
+ <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
+ android:id="@+id/keyguard_indication_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingStart="@dimen/keyguard_indication_text_padding"
+ android:paddingEnd="@dimen/keyguard_indication_text_padding"
+ android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
+ android:accessibilityLiveRegion="polite"/>
+
+ </LinearLayout>
<FrameLayout
android:id="@+id/preview_container"
diff --git a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml b/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
deleted file mode 100644
index cc30a68..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_area_overlay.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/keyguard_indication_area"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom"
- android:layout_gravity="bottom|center_horizontal"
- android:orientation="vertical">
-
- <include layout="@layout/keyguard_indication_text_view" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml b/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
deleted file mode 100644
index 9376b1f..0000000
--- a/packages/SystemUI/res/layout/keyguard_indication_text_view.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
- android:id="@+id/keyguard_indication_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingStart="@dimen/keyguard_indication_text_padding"
- android:paddingEnd="@dimen/keyguard_indication_text_padding"
- android:textAppearance="@style/TextAppearance.Keyguard.BottomArea"
- android:accessibilityLiveRegion="polite"/>
-</merge>
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 2068f7c..dfd9941 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -125,6 +126,7 @@
assertTrue(mHeadsUpManager.canRemoveImmediately(mEntry.getKey()));
}
+ @Ignore("b/141538055")
@Test
public void testCanRemoveImmediately_notTopEntry() {
NotificationEntry laterEntry = new NotificationEntryBuilder()
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 7b35f4d..7e8721d 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -22,13 +22,12 @@
":framework-tethering-shared-srcs",
":net-module-utils-srcs",
":services-tethering-shared-srcs",
- ":servicescore-tethering-src",
],
static_libs: [
"androidx.annotation_annotation",
- "netd_aidl_interface-java",
+ "netd_aidl_interface-unstable-java",
"netlink-client",
- "networkstack-aidl-interfaces-java",
+ "networkstack-aidl-interfaces-unstable-java",
"android.hardware.tetheroffload.control-V1.0-java",
"tethering-client",
],
@@ -41,20 +40,26 @@
defaults: ["TetheringAndroidLibraryDefaults"],
}
-cc_library_shared {
+// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
+cc_library {
name: "libtetheroffloadjni",
srcs: [
"jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
],
shared_libs: [
- "libnativehelper",
- "libcutils",
- "android.hardware.tetheroffload.config@1.0",
+ "libcgrouprc",
+ "libnativehelper_compat_libc++",
+ "libvndksupport",
],
static_libs: [
+ "android.hardware.tetheroffload.config@1.0",
"liblog",
"libbase",
+ "libbinderthreadstate",
+ "libcutils",
"libhidlbase",
+ "libjsoncpp",
+ "libprocessgroup",
"libutils",
],
@@ -64,6 +69,8 @@
"-Wno-unused-parameter",
"-Wthread-safety",
],
+
+ ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
}
// Common defaults for compiling the actual APK.
@@ -71,7 +78,12 @@
name: "TetheringAppDefaults",
platform_apis: true,
privileged: true,
+ // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
+ // explicitly.
jni_libs: [
+ "libcgrouprc",
+ "libnativehelper_compat_libc++",
+ "libvndksupport",
"libtetheroffloadjni",
],
resource_dirs: [
@@ -83,7 +95,16 @@
}
// Non-updatable tethering running in the system server process for devices not using the module
-// TODO: build in-process tethering APK here.
+android_app {
+ name: "InProcessTethering",
+ defaults: ["TetheringAppDefaults"],
+ static_libs: ["TetheringApiCurrentLib"],
+ certificate: "platform",
+ manifest: "AndroidManifest_InProcess.xml",
+ // InProcessTethering is a replacement for Tethering
+ overrides: ["Tethering"],
+ // TODO: use PlatformNetworkPermissionConfig.
+}
// Updatable tethering packaged as an application
android_app {
@@ -96,36 +117,3 @@
// The permission configuration *must* be included to ensure security of the device
required: ["NetworkPermissionConfig"],
}
-
-// This group will be removed when tethering migration is done.
-filegroup {
- name: "tethering-servicescore-srcs",
- srcs: [
- "src/com/android/server/connectivity/tethering/EntitlementManager.java",
- "src/com/android/server/connectivity/tethering/OffloadController.java",
- "src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java",
- "src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
- "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java",
- ],
-}
-
-// This group will be removed when tethering migration is done.
-filegroup {
- name: "tethering-servicesnet-srcs",
- srcs: [
- "src/android/net/dhcp/DhcpServerCallbacks.java",
- "src/android/net/dhcp/DhcpServingParamsParcelExt.java",
- "src/android/net/ip/IpServer.java",
- "src/android/net/ip/RouterAdvertisementDaemon.java",
- "src/android/net/util/InterfaceSet.java",
- "src/android/net/util/PrefixUtils.java",
- ],
-}
-
-// This group would be removed when tethering migration is done.
-filegroup {
- name: "tethering-jni-srcs",
- srcs: [
- "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
- ],
-}
diff --git a/packages/Tethering/AndroidManifest.xml b/packages/Tethering/AndroidManifest.xml
index eb51593..1430ed0 100644
--- a/packages/Tethering/AndroidManifest.xml
+++ b/packages/Tethering/AndroidManifest.xml
@@ -25,5 +25,11 @@
android:process="com.android.networkstack.process"
android:extractNativeLibs="false"
android:persistent="true">
+ <service android:name="com.android.server.connectivity.tethering.TetheringService"
+ android:permission="android.permission.MAINLINE_NETWORK_STACK">
+ <intent-filter>
+ <action android:name="android.net.ITetheringConnector"/>
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/packages/Tethering/AndroidManifest_InProcess.xml b/packages/Tethering/AndroidManifest_InProcess.xml
new file mode 100644
index 0000000..28d405c
--- /dev/null
+++ b/packages/Tethering/AndroidManifest_InProcess.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tethering.inprocess"
+ android:sharedUserId="android.uid.system"
+ android:process="system">
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" />
+ <application>
+ <!-- TODO: Using MAINLINE_NETWORK_STACK instead of NETWORK_STACK when tethering run in the
+ same process with networkstack -->
+ <service android:name="com.android.server.connectivity.tethering.TetheringService"
+ android:process="system"
+ android:permission="android.permission.NETWORK_STACK">
+ <intent-filter>
+ <action android:name="android.net.ITetheringConnector.InProcess"/>
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/packages/Tethering/CleanSpec.mk b/packages/Tethering/CleanSpec.mk
new file mode 100644
index 0000000..70db351
--- /dev/null
+++ b/packages/Tethering/CleanSpec.mk
@@ -0,0 +1,52 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/Tethering)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
new file mode 100644
index 0000000..bca01ebd
--- /dev/null
+++ b/packages/Tethering/apex/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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.
+//
+
+apex {
+ name: "com.android.tethering.apex",
+ apps: ["Tethering"],
+ manifest: "manifest.json",
+ key: "com.android.tethering.apex.key",
+
+ androidManifest: "AndroidManifest.xml",
+}
+
+apex_key {
+ name: "com.android.tethering.apex.key",
+ public_key: "com.android.tethering.apex.avbpubkey",
+ private_key: "com.android.tethering.apex.pem",
+}
+
+android_app_certificate {
+ name: "com.android.tethering.apex.certificate",
+ certificate: "com.android.tethering.apex",
+}
diff --git a/packages/Tethering/apex/AndroidManifest.xml b/packages/Tethering/apex/AndroidManifest.xml
new file mode 100644
index 0000000..7769b79
--- /dev/null
+++ b/packages/Tethering/apex/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tethering.apex">
+ <!-- APEX does not have classes.dex -->
+ <application android:hasCode="false" />
+ <!-- b/145383354: Current minSdk is locked to Q for development cycle, lock it to next version
+ before ship. -->
+ <uses-sdk
+ android:minSdkVersion="29"
+ android:targetSdkVersion="29"
+ />
+</manifest>
diff --git a/packages/Tethering/apex/com.android.tethering.apex.avbpubkey b/packages/Tethering/apex/com.android.tethering.apex.avbpubkey
new file mode 100644
index 0000000..9c87111
--- /dev/null
+++ b/packages/Tethering/apex/com.android.tethering.apex.avbpubkey
Binary files differ
diff --git a/packages/Tethering/apex/com.android.tethering.apex.pem b/packages/Tethering/apex/com.android.tethering.apex.pem
new file mode 100644
index 0000000..a8cd12e
--- /dev/null
+++ b/packages/Tethering/apex/com.android.tethering.apex.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEAwloHpMmwszNBEgUVion141BTvF/oJ5g5DlQIYBtmht4tSpc3
+6elWXd+dhMzFxf/RkxSNRsU+dhD11cPKGp9nUYQQGrHEf3xEKwAHJKRMq26TkJ3o
+1TwOO70TaRKKA4ThNiM3VFDX2vy1ijArhZDIBTGVJCUl9HOHiO+ZJG5DKCx3KXbO
+QWz3c+Lbprr1L76dwIsl5kuoAFwgG0J+9BZhHEzIG1lVpGG7RRLxc8eDIxNN/oKT
+gPYBcOxFYqOECKGBBvElf6MxdRv6xG7gooALY2/HDMYUjAJSOosfwzeymugCzMhK
+e+6CSTAaEfUzuVZvMc2qnd1ly7zpLo9x+TOdH5LEVZpSwqmu2n5bqrUnSEAJUvMz
+SSw0YbsLWJZuTiTV7lecSITgqsmwuZyDexDmUkDQChzrTixsQV6S8vsh/FanjWoi
+zBlPneX8Q7/LME3hxHyLbrabxX0zWiyj8iM9h/8Y4mpO/MjEmmavglTAP4J8zrKD
+FBsntCoch9I49IpYBuO6NfKw1h7AUpLf8gARAjFjRxiJVcSgGY/Wt4/pBzJ57T5g
+xPvqxfpPQP0OA2CT8LqqzZIR8jXs8/TquvwLkkY2kRRPXx+azd5oU2A0uonrUY31
+Bc1obfmWPuEMz9bO/i06ETHuWPd4RiUNaB8qEmjYuKJfhv72YNcRwhrAYJECAwEA
+AQKCAgAaQn3b5yCH5fn5zFQPxvpBP35A6ph8mRXEeNg03B7rRCPMe0gjw9JWlrs6
+0Uw7p4gSnmlEUaxR2ZLN0kmBdV5JZlWitbg+HXU8diGA8u4lD6jCloN6JEYsDi0M
+OmQJe6/OV83HB7FStmh1BnMq9dgA06U6IAbT07RRbUY85OUQDYoAQTw3HNkGgHV7
+PrGYROIdvO9fAYPuoIP6Cu8KXee7Iii7gUOQFWBvQdL7+M4gNCCKrevuNc8WCeaK
+IFvbqq67WGPfrhYlo6UrW2vgqPpg8h5r/GuUS0/+9wNQpjrssUKHltxxiFV0PBqZ
+qI7XkPUvPoG6GMsDT0AWeW1F5ZJqEGPN67Xek0BCD0cpUli+nHD0yWGVHtkpHU2D
+qUOZdB2COfBuXRdW1LsYNPg8YjTCPsmGhISLTwiTNcZJeTxoK1y0CcVW9d7Af2aD
+lYzCegscQlXkSZiFj9s90Vd3KdD2XKrH/ADxzsOxQJ89ka004efdQa5/MKs9aChG
+/5XrwBEfN4O92OjY7KqXUAwB7CcVzNymOjD6r07LM24zbkRpwwXlkP0wmjsHBXkh
+8p0ISmY9QRdvhBgYmFmoPWZncM0zym9LI8atBs4CijQ7JjuOQ8HgHg+Se2eppWfe
+t8r6TVkDB8JeNAMxjX9q0G7icf3JjlIrgERZfyXLmpduR9NdkQKCAQEA5rp2fSKh
+RwihHNtJhNktFJuLR9OA++vyfjqhWnB8CrLPo3//LGWW/+WBr8EwXi/76hQpeKlf
+u8SmkTtxIHlTP2Brh2koh1Qf8HKzPHGjZeDFOoVPKHPqe3nV+cv3srd1mS0Eq3BA
+ZFQq+l61f2iiTZKxDroCahNEa8VMzirW6nKb5xhyMPHXgncCUdphHbwAGatas6be
+RUFg4ChH8BwX6jYw7leRUy2K6OqEl0fckT4Laitlb/ezKtwmD4PPE95q5hH0v3SO
+wetHWafiNrOXPn2wQqBrI2y+AfbTjNmQiaIPgcFKAQ7V3n+c3XfGZ9Xfv4L8m/wo
+RZ4ika1zur021QKCAQEA16OUBPA7BnWd+RJFri2kJBG5JZElaV9chO2ZHcXUbFR9
+HIPkWN19bJbki8Ca0w8FUQuS/M7JeeFjoZ194NlczbR899GVmb0X2AUKXilMacs3
+IONxIDczx3KFtsge8ewXRAjQvgE7M3NpmmJfPLPog7spMCbUIxbc3jzjiZgB/J1s
+WytlUTUY/Zy4V1wujkoydgK2KcHcEWG2oIy7EP0RwnL1NhTksXOtBH6+MoRMAT+H
+fcBK6yfJBNBRQzJ0PdkCCLdQPN1VtwRlWjPXZ3ey4fWvZ399wSLUkM2V1jB4GcOZ
++DAgtwFKs9+HfOdV42GgFWFcjP+bkM3bcdrQFnmYzQKCAQAQnf1KpePXqddwrJpu
+5vVINquhUKpJeoTMcoyMZu2IF7i8nctS9z4Yz/63GcLSBcKu6STTe99ZNqCIdS+A
+lzxXpCoaZoh0tqpWNuyRvd12yOlrfY5l63NH0U6H3xjH1k6x6XwcnMkGcMlnnsqT
+koWd8KKv3NWvrhOPb3ZIou03lWmFC02uGLzcuJWCL6gu7AtVzfGKXspDUqIXgs8r
+i9ptE9oSUFw3EWCfxcQm4RYRn9ZSny1/EufkflZ/Z47Sb4Jjb4ehAlQFw1wwKNcx
++V07MvIu2j7dHkfQ/GXgDwtJ3lIfljwuN1NP4wD5Mlcnw0+KC3UGBvMfkHQM6eEb
+4eTBAoIBAQDWfZsqHlpX3n431XkB+9wdFJP5ThrMaVJ51mxLNRBKgO/BgV+NFSNA
+9AZ5DCf0cCh1qPGYDYhSd2LGywT+trac1j7Hse0AcxpYgQsDBkk/oic/y3wm80HJ
+zZw7Z2uAb7nkrnATzt24G8CbE+ZvVvScs3oQr06raH5hgGdD4bN4No4lUVECKbKl
+8VFbdBHK7vqqb6AKgQ4JLAygPduE1nTn2bkXBklESS98HSXK0dVYGH0JFFBw/63v
+39Y05ObC7iwbx1tEb1RnKzQ1OQO1o1aHc/35ENNhXOfa8ONtneCYn/ty50xjPCG2
+MU1vbBv+hIjbO3D3vvhaXKk+4svAz0qxAoIBAQC84FJEjKHJHx17jLeoTuDfuxwX
+6bOQrI3nHbtnFRvPrMryWRDtHLv89Zma3o68/n4vTn5+AnvgYMZifOYlTlIPxinH
+tlE+qCD8KBXUlZdrc+5GGM18lp5tF3Ro4LireH+OhiOAWawaSzDIDYdiR6Kz9NU+
+SjcHKjDObeM6iMEukoaRsufMedpUSrnbzMraAJgBZGay1NZs/o8Icl3OySYPZWEK
+MJxVBMXU9QcUp2GEioYd/eNuP9rwyjq/EIUDJbP2vESAe6+FdGbIgvyYTV/gnKaH
+GcvyMNVZbCMp/wCYNonjlu+18m2w+pVs2uuZLqORkrKYhisK83TKxh4YOWJh
+-----END RSA PRIVATE KEY-----
diff --git a/packages/Tethering/apex/com.android.tethering.apex.pk8 b/packages/Tethering/apex/com.android.tethering.apex.pk8
new file mode 100644
index 0000000..5663246
--- /dev/null
+++ b/packages/Tethering/apex/com.android.tethering.apex.pk8
Binary files differ
diff --git a/packages/Tethering/apex/com.android.tethering.apex.x509.pem b/packages/Tethering/apex/com.android.tethering.apex.x509.pem
new file mode 100644
index 0000000..a5e9401
--- /dev/null
+++ b/packages/Tethering/apex/com.android.tethering.apex.x509.pem
@@ -0,0 +1,36 @@
+-----BEGIN CERTIFICATE-----
+MIIGMzCCBBugAwIBAgIUXVtoDaXanhs7ma8VIICambMkj5UwDQYJKoZIhvcNAQEL
+BQAwgacxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
+DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
+b2lkMSMwIQYDVQQDDBpjb20uYW5kcm9pZC50ZXRoZXJpbmcuYXBleDEiMCAGCSqG
+SIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAgFw0xOTExMjgwNjU4MTRaGA80
+NzU3MTAyNDA2NTgxNFowgacxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9y
+bmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAw
+DgYDVQQLDAdBbmRyb2lkMSMwIQYDVQQDDBpjb20uYW5kcm9pZC50ZXRoZXJpbmcu
+YXBleDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJ
+KoZIhvcNAQEBBQADggIPADCCAgoCggIBANwzufMBdOj9XlNwiX+bXl/94G0DklWW
+nzob0jPlubCFfRqYkjCf2eOd28Mu/O1pOBcvobnrs9OTpGzcHkz2h58L5/0UMVTS
+tBugwCE49XF5FHawqVHNZE+s5tDmnp2cufhNc5HXHY4oZKh80/WVdbcKxiLjSY2T
+PgRAfB6E6XByKD3t1cSsc3liRVKADoJOVDvmF+xnyvSV/SN38bvTQk9aVs95mj0W
+yov6gzXBnqN7iQlvkhcijZBnFWxvoNbJ5KFy1abYOrm+ueXje4BcNhVOeRMb4E9N
+eo7+9k1GEI7TYG7laNNcp7UJ1IXCJzv/wBFKRg3f1HB3unKfx2rtKerDnVsr3o7V
+KProkgRNKNhhQ6opNguiH1YMzKpWMaC988n4AQPryPdIOmVIxIC5jJrixdxgzDXT
+qeiwFiXis291uyls08B03PQFlY9oWaY9P8s+4hIUjB6rLl+XZXsLDtDFxXeJ97NB
+8XZN1gBJoBoLknFs0C4LKpmJZB/EBao9tXV9dL/5lydRo6HzQDpjW8QX06CTUM6z
+Lr3LVelhqbsuZsV42yBKl+/LfrvNjBLEPdSevt2oMrlJW7m4iSNaMtDtJ2Oy8fA5
+WSIgLWuMbkaFDza3JzwiMzxbtbJHYiy6rY7aVywo3Vqwr1+KO3cq4eLQq62zUjRY
+e6KJwvgE2YmpAgMBAAGjUzBRMB0GA1UdDgQWBBQ8h1oF5JfKFmJCN8nfimbUK+IR
+wjAfBgNVHSMEGDAWgBQ8h1oF5JfKFmJCN8nfimbUK+IRwjAPBgNVHRMBAf8EBTAD
+AQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAP5hIkAxSyt9hRafMKiFlmXcL277bgoxNd
+qGZYbdcCFjfvM2r0QQcM/K7x2ZslFe7mJSzcyMifpm4YQTEo2yYbzKItXQV+eV1K
+9RNksRGFG9umsdWaSHhfQmcmxtr2nu9rGAgxy5OQFtyXmJPUPEM2cb/YeILwYhuQ
+Ux3kaj/fxGltX1JBag7HnMzCTZK++fRo5nqFVOJQgJH8ZpuzGeM9kZvP1+b55046
+PhSnlqmZoKhG4i5POPvvZvaakh/lM3x/N7lIlSaQpCGf7jmldni4L0/GenULVKzH
+iN73aBfh4GEvE0HRcOoH3L7V6kc3WMMLve0chZBHpoVYbzUJEJOUL4yrmwEehqtf
+xm4vlYg3vqtcE3UnU/UGdMb16t77Nz88LlpBY5ierIt0jZMU0M81ppRhr1uiD2Lj
+091sEA0Bxcw/6Q8QNF2eR7SG7Qwipnms+lw6Vcxve+7DdTrdEA0k3XgpdXp8Ya+2
+PAp9SLVp1UHiGq3qD9Jvm34QmlUWAIUTHZs3DSgs1y3K5eyw/cnzTvUUOljc/n2y
+VF0FFZtJ1dVLrzQ80Ik7apEXpBqkgBGV04/L3QYk4C0/sP+1yk6zjeeeAvDtUcHS
+gLtjAfacQl/kwfVQWfrF7VByLcivApC6EUdvT3cURM5DfZRQ4RcKr1D61VYPnNRH
++/NVbMObwQ==
+-----END CERTIFICATE-----
diff --git a/packages/Tethering/apex/manifest.json b/packages/Tethering/apex/manifest.json
new file mode 100644
index 0000000..3fb62f3
--- /dev/null
+++ b/packages/Tethering/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.tethering.apex",
+ "version": 290000000
+}
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 5b01b1e..adc5a72 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -18,8 +18,12 @@
aidl_interface {
name: "tethering-aidl-interfaces",
local_include_dir: "src",
+ include_dirs: ["frameworks/base/core/java"], // For framework parcelables.
srcs: [
+ "src/android/net/ITetherInternalCallback.aidl",
"src/android/net/ITetheringConnector.aidl",
+ "src/android/net/TetheringConfigurationParcel.aidl",
+ "src/android/net/TetherStatesParcel.aidl",
],
backend: {
ndk: {
@@ -33,8 +37,15 @@
java_library {
name: "tethering-client",
- platform_apis: true,
+ sdk_version: "system_current",
static_libs: [
"tethering-aidl-interfaces-java",
],
}
+
+// This is temporary file group which would be removed after TetheringManager is built
+// into tethering-client. Will be done by aosp/1156906.
+filegroup {
+ name: "tethering-manager",
+ srcs: ["src/android/net/TetheringManager.java"],
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
new file mode 100644
index 0000000..abb00e8
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetherInternalCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.Network;
+import android.net.TetheringConfigurationParcel;
+import android.net.TetherStatesParcel;
+
+/**
+ * Callback class for receiving tethering changed events
+ * @hide
+ */
+oneway interface ITetherInternalCallback
+{
+ void onUpstreamChanged(in Network network);
+ void onConfigurationChanged(in TetheringConfigurationParcel config);
+ void onTetherStatesChanged(in TetherStatesParcel states);
+ void onCallbackCreated(in Network network, in TetheringConfigurationParcel config,
+ in TetherStatesParcel states);
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index 443481e..bfe502f 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -15,6 +15,23 @@
*/
package android.net;
+import android.net.ITetherInternalCallback;
+import android.os.ResultReceiver;
+
/** @hide */
oneway interface ITetheringConnector {
+ void tether(String iface);
+
+ void untether(String iface);
+
+ void setUsbTethering(boolean enable);
+
+ void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi);
+
+ void stopTethering(int type);
+
+ void requestLatestTetheringEntitlementResult(int type, in ResultReceiver receiver,
+ boolean showEntitlementUi);
+
+ void registerTetherInternalCallback(ITetherInternalCallback callback);
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
new file mode 100644
index 0000000..3d842b3
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetherStatesParcel.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.net;
+
+/**
+ * Status details for tethering downstream interfaces.
+ * {@hide}
+ */
+parcelable TetherStatesParcel {
+ String[] availableList;
+ String[] tetheredList;
+ String[] localOnlyList;
+ String[] erroredIfaceList;
+ // List of Last error code corresponding to each errored iface in erroredIfaceList. */
+ // TODO: Improve this as b/143122247.
+ int[] lastErrorList;
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
new file mode 100644
index 0000000..89f3813
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConfigurationParcel.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.net;
+
+/**
+ * Configuration details for tethering.
+ * @hide
+ */
+parcelable TetheringConfigurationParcel {
+ int subId;
+ String[] tetherableUsbRegexs;
+ String[] tetherableWifiRegexs;
+ String[] tetherableBluetoothRegexs;
+ boolean isDunRequired;
+ boolean chooseUpstreamAutomatically;
+ int[] preferredUpstreamIfaceTypes;
+ String[] legacyDhcpRanges;
+ String[] defaultIPv4DNS;
+ boolean enableLegacyDhcpServer;
+ String[] provisioningApp;
+ String provisioningAppNoUi;
+ int provisioningCheckPeriod;
+}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
new file mode 100644
index 0000000..eb0d443
--- /dev/null
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -0,0 +1,513 @@
+/*
+ * 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.net;
+
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.util.SharedLog;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+/**
+ * Service used to communicate with the tethering, which is running in a separate module.
+ * @hide
+ */
+public class TetheringManager {
+ private static final String TAG = TetheringManager.class.getSimpleName();
+
+ private static TetheringManager sInstance;
+
+ @Nullable
+ private ITetheringConnector mConnector;
+ private TetherInternalCallback mCallback;
+ private Network mTetherUpstream;
+ private TetheringConfigurationParcel mTetheringConfiguration;
+ private TetherStatesParcel mTetherStatesParcel;
+
+ private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
+ new RemoteCallbackList<>();
+ @GuardedBy("mLog")
+ private final SharedLog mLog = new SharedLog(TAG);
+
+ private TetheringManager() { }
+
+ /**
+ * Get the TetheringManager singleton instance.
+ */
+ public static synchronized TetheringManager getInstance() {
+ if (sInstance == null) {
+ sInstance = new TetheringManager();
+ }
+ return sInstance;
+ }
+
+ private class TetheringConnection implements
+ ConnectivityModuleConnector.ModuleServiceCallback {
+ @Override
+ public void onModuleServiceConnected(@NonNull IBinder service) {
+ logi("Tethering service connected");
+ registerTetheringService(service);
+ }
+ }
+
+ private void registerTetheringService(@NonNull IBinder service) {
+ final ITetheringConnector connector = ITetheringConnector.Stub.asInterface(service);
+
+ log("Tethering service registered");
+
+ // Currently TetheringManager instance is only used by ConnectivityService and mConnector
+ // only expect to assign once when system server start and bind tethering service.
+ // STOPSHIP: Change mConnector to final before TetheringManager put into boot classpath.
+ mConnector = connector;
+ mCallback = new TetherInternalCallback();
+ try {
+ mConnector.registerTetherInternalCallback(mCallback);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private class TetherInternalCallback extends ITetherInternalCallback.Stub {
+ private final ConditionVariable mWaitForCallback = new ConditionVariable(false);
+ private static final int EVENT_CALLBACK_TIMEOUT_MS = 60_000;
+
+ @Override
+ public void onUpstreamChanged(Network network) {
+ mTetherUpstream = network;
+ reportUpstreamChanged(network);
+ }
+
+ @Override
+ public void onConfigurationChanged(TetheringConfigurationParcel config) {
+ mTetheringConfiguration = config;
+ }
+
+ @Override
+ public void onTetherStatesChanged(TetherStatesParcel states) {
+ mTetherStatesParcel = states;
+ }
+
+ @Override
+ public void onCallbackCreated(Network network, TetheringConfigurationParcel config,
+ TetherStatesParcel states) {
+ mTetherUpstream = network;
+ mTetheringConfiguration = config;
+ mTetherStatesParcel = states;
+ mWaitForCallback.open();
+ }
+
+ boolean awaitCallbackCreation() {
+ return mWaitForCallback.block(EVENT_CALLBACK_TIMEOUT_MS);
+ }
+ }
+
+ private void reportUpstreamChanged(Network network) {
+ final int length = mTetheringEventCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
+ } catch (RemoteException e) {
+ // Not really very much to do here.
+ }
+ }
+ } finally {
+ mTetheringEventCallbacks.finishBroadcast();
+ }
+ }
+
+ /**
+ * Start the tethering service. Should be called only once on device startup.
+ *
+ * <p>This method will start the tethering service either in the network stack process,
+ * or inside the system server on devices that do not support the tethering module.
+ *
+ * {@hide}
+ */
+ public void start() {
+ // Using MAINLINE_NETWORK_STACK permission after cutting off the dpendency of system server.
+ ConnectivityModuleConnector.getInstance().startModuleService(
+ ITetheringConnector.class.getName(), NETWORK_STACK,
+ new TetheringConnection());
+ log("Tethering service start requested");
+ }
+
+ /**
+ * Attempt to tether the named interface. This will setup a dhcp server
+ * on the interface, forward and NAT IP v4 packets and forward DNS requests
+ * to the best active upstream network interface. Note that if no upstream
+ * IP network interface is available, dhcp will still run and traffic will be
+ * allowed between the tethered devices and this device, though upstream net
+ * access will of course fail until an upstream network interface becomes
+ * active. Note: return value do not have any meaning. It is better to use
+ * #getTetherableIfaces() to ensure corresponding interface is available for
+ * tethering before calling #tether().
+ *
+ * @deprecated The only usages should be in PanService and Wifi P2P which
+ * need direct access.
+ *
+ * {@hide}
+ */
+ @Deprecated
+ public int tether(@NonNull String iface) {
+ try {
+ mConnector.tether(iface);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return TETHER_ERROR_NO_ERROR;
+ }
+
+ /**
+ * Stop tethering the named interface.
+ *
+ * @deprecated
+ * {@hide}
+ */
+ @Deprecated
+ public int untether(@NonNull String iface) {
+ try {
+ mConnector.untether(iface);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return TETHER_ERROR_NO_ERROR;
+ }
+
+ /**
+ * Attempt to both alter the mode of USB and Tethering of USB. WARNING: New client should not
+ * use this API anymore. All clients should use #startTethering or #stopTethering which
+ * encapsulate proper entitlement logic. If the API is used and an entitlement check is needed,
+ * downstream USB tethering will be enabled but will not have any upstream.
+ *
+ * @deprecated
+ * {@hide}
+ */
+ @Deprecated
+ public int setUsbTethering(boolean enable) {
+ try {
+ mConnector.setUsbTethering(enable);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return TETHER_ERROR_NO_ERROR;
+ }
+
+ /**
+ * Starts tethering and runs tether provisioning for the given type if needed. If provisioning
+ * fails, stopTethering will be called automatically.
+ *
+ * {@hide}
+ */
+ // TODO: improve the usage of ResultReceiver, b/145096122
+ public void startTethering(int type, @NonNull ResultReceiver receiver,
+ boolean showProvisioningUi) {
+ try {
+ mConnector.startTethering(type, receiver, showProvisioningUi);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if
+ * applicable.
+ *
+ * {@hide}
+ */
+ public void stopTethering(int type) {
+ try {
+ mConnector.stopTethering(type);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request the latest value of the tethering entitlement check.
+ *
+ * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
+ * out some such apps are observed to abuse this API, change to per-UID limits on this API
+ * if it's really needed.
+ */
+ // TODO: improve the usage of ResultReceiver, b/145096122
+ public void requestLatestTetheringEntitlementResult(int type, @NonNull ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ try {
+ mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Register tethering event callback.
+ *
+ * {@hide}
+ */
+ public void registerTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
+ mTetheringEventCallbacks.register(callback);
+ }
+
+ /**
+ * Unregister tethering event callback.
+ *
+ * {@hide}
+ */
+ public void unregisterTetheringEventCallback(@NonNull ITetheringEventCallback callback) {
+ mTetheringEventCallbacks.unregister(callback);
+ }
+
+ /**
+ * Get a more detailed error code after a Tethering or Untethering
+ * request asynchronously failed.
+ *
+ * {@hide}
+ */
+ public int getLastTetherError(@NonNull String iface) {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
+
+ int i = 0;
+ for (String errored : mTetherStatesParcel.erroredIfaceList) {
+ if (iface.equals(errored)) return mTetherStatesParcel.lastErrorList[i];
+
+ i++;
+ }
+ return TETHER_ERROR_NO_ERROR;
+ }
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * USB network interfaces. If USB tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetherableUsbRegexs() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ return mTetheringConfiguration.tetherableUsbRegexs;
+ }
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * Wifi network interfaces. If Wifi tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetherableWifiRegexs() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ return mTetheringConfiguration.tetherableWifiRegexs;
+ }
+
+ /**
+ * Get the list of regular expressions that define any tetherable
+ * Bluetooth network interfaces. If Bluetooth tethering is not supported by the
+ * device, this list should be empty.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetherableBluetoothRegexs() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ return mTetheringConfiguration.tetherableBluetoothRegexs;
+ }
+
+ /**
+ * Get the set of tetherable, available interfaces. This list is limited by
+ * device configuration and current interface existence.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetherableIfaces() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ if (mTetherStatesParcel == null) return new String[0];
+ return mTetherStatesParcel.availableList;
+ }
+
+ /**
+ * Get the set of tethered interfaces.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetheredIfaces() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ if (mTetherStatesParcel == null) return new String[0];
+ return mTetherStatesParcel.tetheredList;
+ }
+
+ /**
+ * Get the set of interface names which attempted to tether but
+ * failed.
+ *
+ * {@hide}
+ */
+ public @NonNull String[] getTetheringErroredIfaces() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ if (mTetherStatesParcel == null) return new String[0];
+ return mTetherStatesParcel.erroredIfaceList;
+ }
+
+ /**
+ * Get the set of tethered dhcp ranges.
+ *
+ * @deprecated This API just return the default value which is not used in DhcpServer.
+ * {@hide}
+ */
+ @Deprecated
+ public @NonNull String[] getTetheredDhcpRanges() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ return mTetheringConfiguration.legacyDhcpRanges;
+ }
+
+ /**
+ * Check if the device allows for tethering.
+ *
+ * {@hide}
+ */
+ public boolean hasTetherableConfiguration() {
+ if (!mCallback.awaitCallbackCreation()) {
+ throw new NullPointerException("callback was not ready yet");
+ }
+ final boolean hasDownstreamConfiguration =
+ (mTetheringConfiguration.tetherableUsbRegexs.length != 0)
+ || (mTetheringConfiguration.tetherableWifiRegexs.length != 0)
+ || (mTetheringConfiguration.tetherableBluetoothRegexs.length != 0);
+ final boolean hasUpstreamConfiguration =
+ (mTetheringConfiguration.preferredUpstreamIfaceTypes.length != 0)
+ || mTetheringConfiguration.chooseUpstreamAutomatically;
+
+ return hasDownstreamConfiguration && hasUpstreamConfiguration;
+ }
+
+ /**
+ * Log a message in the local log.
+ */
+ private void log(@NonNull String message) {
+ synchronized (mLog) {
+ mLog.log(message);
+ }
+ }
+
+ /**
+ * Log a condition that should never happen.
+ */
+ private void logWtf(@NonNull String message, @Nullable Throwable e) {
+ Slog.wtf(TAG, message);
+ synchronized (mLog) {
+ mLog.e(message, e);
+ }
+ }
+
+ /**
+ * Log a ERROR level message in the local and system logs.
+ */
+ private void loge(@NonNull String message, @Nullable Throwable e) {
+ synchronized (mLog) {
+ mLog.e(message, e);
+ }
+ }
+
+ /**
+ * Log a INFO level message in the local and system logs.
+ */
+ private void logi(@NonNull String message) {
+ synchronized (mLog) {
+ mLog.i(message);
+ }
+ }
+
+ /**
+ * Dump TetheringManager logs to the specified {@link PrintWriter}.
+ */
+ public void dump(@NonNull PrintWriter pw) {
+ // dump is thread-safe on SharedLog
+ mLog.dump(null, pw, null);
+
+ pw.print("subId: ");
+ pw.println(mTetheringConfiguration.subId);
+
+ dumpStringArray(pw, "tetherableUsbRegexs",
+ mTetheringConfiguration.tetherableUsbRegexs);
+ dumpStringArray(pw, "tetherableWifiRegexs",
+ mTetheringConfiguration.tetherableWifiRegexs);
+ dumpStringArray(pw, "tetherableBluetoothRegexs",
+ mTetheringConfiguration.tetherableBluetoothRegexs);
+
+ pw.print("isDunRequired: ");
+ pw.println(mTetheringConfiguration.isDunRequired);
+
+ pw.print("chooseUpstreamAutomatically: ");
+ pw.println(mTetheringConfiguration.chooseUpstreamAutomatically);
+
+ dumpStringArray(pw, "legacyDhcpRanges", mTetheringConfiguration.legacyDhcpRanges);
+ dumpStringArray(pw, "defaultIPv4DNS", mTetheringConfiguration.defaultIPv4DNS);
+
+ dumpStringArray(pw, "provisioningApp", mTetheringConfiguration.provisioningApp);
+ pw.print("provisioningAppNoUi: ");
+ pw.println(mTetheringConfiguration.provisioningAppNoUi);
+
+ pw.print("enableLegacyDhcpServer: ");
+ pw.println(mTetheringConfiguration.enableLegacyDhcpServer);
+
+ pw.println();
+ }
+
+ private static void dumpStringArray(@NonNull PrintWriter pw, @NonNull String label,
+ @Nullable String[] values) {
+ pw.print(label);
+ pw.print(": ");
+
+ if (values != null) {
+ final StringJoiner sj = new StringJoiner(", ", "[", "]");
+ for (String value : values) sj.add(value);
+
+ pw.print(sj.toString());
+ } else {
+ pw.print("null");
+ }
+
+ pw.println();
+ }
+}
diff --git a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
index 3eaf488..663154a 100644
--- a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ b/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -145,4 +145,18 @@
gMethods, NELEM(gMethods));
}
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("ERROR: GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+}
+
}; // namespace android
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-hdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-ldpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-ldpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-mdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-xhdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-xhdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_general.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_general.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_general.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_sys_tether_usb.png b/packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
similarity index 100%
rename from core/res/res/drawable-xxhdpi/stat_sys_tether_usb.png
rename to packages/Tethering/res/drawable-xxhdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/packages/Tethering/res/values-af/strings.xml b/packages/Tethering/res/values-af/strings.xml
new file mode 100644
index 0000000..1258805
--- /dev/null
+++ b/packages/Tethering/res/values-af/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Verbinding of Wi-Fi-warmkol aktief"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om op te stel."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Verbinding is gedeaktiveer"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontak jou administrateur vir besonderhede"</string>
+</resources>
diff --git a/packages/Tethering/res/values-am/strings.xml b/packages/Tethering/res/values-am/strings.xml
new file mode 100644
index 0000000..9c36192
--- /dev/null
+++ b/packages/Tethering/res/values-am/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"መሰካት ወይም ገባሪ ድረስ ነጥብ"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ለማዋቀር መታ ያድርጉ።"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"እንደ ሞደም መሰካት ተሰናክሏል"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ለዝርዝሮች የእርስዎን አስተዳዳሪ ያነጋግሩ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ar/strings.xml b/packages/Tethering/res/values-ar/strings.xml
new file mode 100644
index 0000000..9f84ce4
--- /dev/null
+++ b/packages/Tethering/res/values-ar/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"النطاق أو نقطة الاتصال نشطة"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"انقر للإعداد."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"تم إيقاف التوصيل"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"اتصل بالمشرف للحصول على التفاصيل"</string>
+</resources>
diff --git a/packages/Tethering/res/values-as/strings.xml b/packages/Tethering/res/values-as/strings.xml
new file mode 100644
index 0000000..8855822
--- /dev/null
+++ b/packages/Tethering/res/values-as/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"টেডাৰিং বা হটস্প\'ট সক্ৰিয় অৱস্থাত আছে"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ছেট আপ কৰিবলৈ টিপক।"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"টেডাৰিং অক্ষম কৰি থোৱা হৈছে"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"সবিশেষ জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক"</string>
+</resources>
diff --git a/packages/Tethering/res/values-az/strings.xml b/packages/Tethering/res/values-az/strings.xml
new file mode 100644
index 0000000..eba50eb
--- /dev/null
+++ b/packages/Tethering/res/values-az/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tezerinq və ya hotspot aktivdir"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Quraşdırmaq üçün tıklayın."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Birləşmə deaktivdir"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Məlumat üçün adminlə əlaqə saxlayın"</string>
+</resources>
diff --git a/packages/Tethering/res/values-b+sr+Latn/strings.xml b/packages/Tethering/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..5b0e488
--- /dev/null
+++ b/packages/Tethering/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivno povezivanje sa internetom preko mobilnog uređaja ili hotspot"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste podesili."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Privezivanje je onemogućeno"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Potražite detalje od administratora"</string>
+</resources>
diff --git a/packages/Tethering/res/values-be/strings.xml b/packages/Tethering/res/values-be/strings.xml
new file mode 100644
index 0000000..5966c71
--- /dev/null
+++ b/packages/Tethering/res/values-be/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"USB-мадэм або хот-спот Wi-Fi актыўныя"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Дакраніцеся, каб наладзіць."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Рэжым мадэма адключаны"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Звярніцеся да адміністратара па падрабязную інфармацыю"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bg/strings.xml b/packages/Tethering/res/values-bg/strings.xml
new file mode 100644
index 0000000..ed58d73
--- /dev/null
+++ b/packages/Tethering/res/values-bg/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Има активна споделена връзка или безжична точка за достъп"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Докоснете, за да настроите."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Функцията за тетъринг е деактивирана"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Свържете се с администратора си за подробности"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bn/strings.xml b/packages/Tethering/res/values-bn/strings.xml
new file mode 100644
index 0000000..8d9880a
--- /dev/null
+++ b/packages/Tethering/res/values-bn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"টিথারিং বা হটস্পট সক্রিয় আছে"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"সেট-আপ করার জন্য আলতো চাপুন৷"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"টিথারিং অক্ষম করা আছে"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"বিশদ বিবরণের জন্য প্রশাসকের সাথে যোগাযোগ করুন"</string>
+</resources>
diff --git a/packages/Tethering/res/values-bs/strings.xml b/packages/Tethering/res/values-bs/strings.xml
new file mode 100644
index 0000000..2361b9d
--- /dev/null
+++ b/packages/Tethering/res/values-bs/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Uređaj dijeli vezu ili djeluje kao pristupna tačka"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite za postavke"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezivanje putem mobitela je onemogućeno"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontaktirajte svog administratora za dodatne detalje"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ca/strings.xml b/packages/Tethering/res/values-ca/strings.xml
new file mode 100644
index 0000000..6752b51
--- /dev/null
+++ b/packages/Tethering/res/values-ca/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Compartició de xarxa o punt d\'accés Wi-Fi activat"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Toca per configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"La compartició de xarxa està desactivada"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta amb el teu administrador per obtenir més informació"</string>
+</resources>
diff --git a/packages/Tethering/res/values-cs/strings.xml b/packages/Tethering/res/values-cs/strings.xml
new file mode 100644
index 0000000..5fdd53a
--- /dev/null
+++ b/packages/Tethering/res/values-cs/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Sdílené připojení nebo hotspot je aktivní."</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím zahájíte nastavení."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je zakázán"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požádejte administrátora"</string>
+</resources>
diff --git a/packages/Tethering/res/values-da/strings.xml b/packages/Tethering/res/values-da/strings.xml
new file mode 100644
index 0000000..2775dfa
--- /dev/null
+++ b/packages/Tethering/res/values-da/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Netdeling eller hotspot er aktivt"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tryk for at konfigurere"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Netdeling er deaktiveret"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakt din administrator for at få oplysninger"</string>
+</resources>
diff --git a/packages/Tethering/res/values-de/strings.xml b/packages/Tethering/res/values-de/strings.xml
new file mode 100644
index 0000000..9046cd5
--- /dev/null
+++ b/packages/Tethering/res/values-de/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oder Hotspot aktiv"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Zum Einrichten tippen."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering ist deaktiviert"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Bitte wende dich für weitere Informationen an den Administrator"</string>
+</resources>
diff --git a/packages/Tethering/res/values-el/strings.xml b/packages/Tethering/res/values-el/strings.xml
new file mode 100644
index 0000000..3b9f537
--- /dev/null
+++ b/packages/Tethering/res/values-el/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Πρόσδεση ή σύνδεση σημείου πρόσβασης ενεργή"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Πατήστε για ρύθμιση."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Η σύνδεση είναι απενεργοποιημένη"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Επικοινωνήστε με τον διαχειριστή σας για λεπτομέρειες"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rAU/strings.xml b/packages/Tethering/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rAU/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rCA/strings.xml b/packages/Tethering/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rCA/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rGB/strings.xml b/packages/Tethering/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rGB/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rIN/strings.xml b/packages/Tethering/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..56b88a5
--- /dev/null
+++ b/packages/Tethering/res/values-en-rIN/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-en-rXC/strings.xml b/packages/Tethering/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..7f47fc8
--- /dev/null
+++ b/packages/Tethering/res/values-en-rXC/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering or hotspot active"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tap to set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is disabled"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contact your admin for details"</string>
+</resources>
diff --git a/packages/Tethering/res/values-es-rUS/strings.xml b/packages/Tethering/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..e4618b8
--- /dev/null
+++ b/packages/Tethering/res/values-es-rUS/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Anclaje a red o zona activa conectados"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Presiona para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Se inhabilitó la conexión mediante dispositivo portátil"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Para obtener más información, comunícate con el administrador"</string>
+</resources>
diff --git a/packages/Tethering/res/values-es/strings.xml b/packages/Tethering/res/values-es/strings.xml
new file mode 100644
index 0000000..8dc1575
--- /dev/null
+++ b/packages/Tethering/res/values-es/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"La conexión compartida está inhabilitada"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ponte en contacto con el administrador para obtener más información"</string>
+</resources>
diff --git a/packages/Tethering/res/values-et/strings.xml b/packages/Tethering/res/values-et/strings.xml
new file mode 100644
index 0000000..872c8a7
--- /dev/null
+++ b/packages/Tethering/res/values-et/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Jagamine või kuumkoht on aktiivne"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Puudutage seadistamiseks."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Jagamine on keelatud"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lisateabe saamiseks võtke ühendust oma administraatoriga"</string>
+</resources>
diff --git a/packages/Tethering/res/values-eu/strings.xml b/packages/Tethering/res/values-eu/strings.xml
new file mode 100644
index 0000000..6c4605e
--- /dev/null
+++ b/packages/Tethering/res/values-eu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Konexioa partekatzea edo sare publikoa aktibo"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Sakatu konfiguratzeko."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Desgaituta dago konexioa partekatzeko aukera"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xehetasunak lortzeko, jarri administratzailearekin harremanetan"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fa/strings.xml b/packages/Tethering/res/values-fa/strings.xml
new file mode 100644
index 0000000..bc2ee23
--- /dev/null
+++ b/packages/Tethering/res/values-fa/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"اشتراکگذاری اینترنت یا نقطه اتصال فعال"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"برای راهاندازی ضربه بزنید."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"اشتراکگذاری اینترنت غیرفعال است"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"برای جزئیات، با سرپرستتان تماس بگیرید"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fi/strings.xml b/packages/Tethering/res/values-fi/strings.xml
new file mode 100644
index 0000000..ff0fca6
--- /dev/null
+++ b/packages/Tethering/res/values-fi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Internetin jakaminen tai yhteyspiste käytössä"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Määritä napauttamalla."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Yhteyden jakaminen poistettu käytöstä"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kysy lisätietoja järjestelmänvalvojalta."</string>
+</resources>
diff --git a/packages/Tethering/res/values-fr-rCA/strings.xml b/packages/Tethering/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..1f5df0e
--- /dev/null
+++ b/packages/Tethering/res/values-fr-rCA/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Touchez pour configurer."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Communiquez avec votre administrateur pour obtenir plus de détails"</string>
+</resources>
diff --git a/packages/Tethering/res/values-fr/strings.xml b/packages/Tethering/res/values-fr/strings.xml
new file mode 100644
index 0000000..daf7c9d
--- /dev/null
+++ b/packages/Tethering/res/values-fr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Partage de connexion ou point d\'accès sans fil activé"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Appuyez ici pour configurer."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Le partage de connexion est désactivé"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Pour en savoir plus, contactez votre administrateur"</string>
+</resources>
diff --git a/packages/Tethering/res/values-gl/strings.xml b/packages/Tethering/res/values-gl/strings.xml
new file mode 100644
index 0000000..0d16a1d
--- /dev/null
+++ b/packages/Tethering/res/values-gl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Conexión compartida ou zona wifi activada"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tocar para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"A conexión compartida está desactivada"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacta co administrador para obter información"</string>
+</resources>
diff --git a/packages/Tethering/res/values-gu/strings.xml b/packages/Tethering/res/values-gu/strings.xml
new file mode 100644
index 0000000..9d6b02f
--- /dev/null
+++ b/packages/Tethering/res/values-gu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ટિથરિંગ અથવા હૉટસ્પૉટ સક્રિય"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"સેટ કરવા માટે ટૅપ કરો."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ટિથરિંગ અક્ષમ કરેલ છે"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"વિગતો માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hi/strings.xml b/packages/Tethering/res/values-hi/strings.xml
new file mode 100644
index 0000000..9c29d9a
--- /dev/null
+++ b/packages/Tethering/res/values-hi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग या हॉटस्पॉट सक्रिय"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करने के लिए टैप करें."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग अक्षम है"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"जानकारी के लिए अपने एडमिन से संपर्क करें"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hr/strings.xml b/packages/Tethering/res/values-hr/strings.xml
new file mode 100644
index 0000000..d0d25bb
--- /dev/null
+++ b/packages/Tethering/res/values-hr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ograničenje ili aktivan hotspot"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Dodirnite da biste postavili."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modemsko je povezivanje onemogućeno"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Obratite se administratoru da biste saznali pojedinosti"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hu/strings.xml b/packages/Tethering/res/values-hu/strings.xml
new file mode 100644
index 0000000..3129659
--- /dev/null
+++ b/packages/Tethering/res/values-hu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Megosztás vagy aktív hotspot"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Koppintson a beállításhoz."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Az internetmegosztás le van tiltva"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"A részletekért forduljon rendszergazdájához"</string>
+</resources>
diff --git a/packages/Tethering/res/values-hy/strings.xml b/packages/Tethering/res/values-hy/strings.xml
new file mode 100644
index 0000000..8ba6435
--- /dev/null
+++ b/packages/Tethering/res/values-hy/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Մոդեմի ռեժիմը միացված է"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Հպեք՝ կարգավորելու համար:"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Մոդեմի ռեժիմն անջատված է"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Մանրամասների համար դիմեք ձեր ադմինիստրատորին"</string>
+</resources>
diff --git a/packages/Tethering/res/values-in/strings.xml b/packages/Tethering/res/values-in/strings.xml
new file mode 100644
index 0000000..1e093ab
--- /dev/null
+++ b/packages/Tethering/res/values-in/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering (Penambatan) atau hotspot aktif"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Ketuk untuk menyiapkan."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering dinonaktifkan"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi admin untuk mengetahui detailnya"</string>
+</resources>
diff --git a/packages/Tethering/res/values-is/strings.xml b/packages/Tethering/res/values-is/strings.xml
new file mode 100644
index 0000000..f5769d5
--- /dev/null
+++ b/packages/Tethering/res/values-is/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Kveikt á tjóðrun eða aðgangsstað"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Ýttu til að setja upp."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Slökkt er á tjóðrun"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hafðu samband við kerfisstjórann til að fá upplýsingar"</string>
+</resources>
diff --git a/packages/Tethering/res/values-it/strings.xml b/packages/Tethering/res/values-it/strings.xml
new file mode 100644
index 0000000..e0b3724
--- /dev/null
+++ b/packages/Tethering/res/values-it/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering oppure hotspot attivo"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tocca per impostare."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering disattivato"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contatta il tuo amministratore per avere informazioni dettagliate"</string>
+</resources>
diff --git a/packages/Tethering/res/values-iw/strings.xml b/packages/Tethering/res/values-iw/strings.xml
new file mode 100644
index 0000000..c002c44
--- /dev/null
+++ b/packages/Tethering/res/values-iw/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"שיתוף אינטרנט פעיל"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"הקש כדי להגדיר."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"שיתוף האינטרנט בין ניידים מושבת"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"לפרטים, יש לפנות למנהל המערכת"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ja/strings.xml b/packages/Tethering/res/values-ja/strings.xml
new file mode 100644
index 0000000..314bde0
--- /dev/null
+++ b/packages/Tethering/res/values-ja/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"テザリングまたはアクセスポイントが有効です"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"タップしてセットアップします。"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"テザリングは無効に設定されています"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳しくは、管理者にお問い合わせください"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ka/strings.xml b/packages/Tethering/res/values-ka/strings.xml
new file mode 100644
index 0000000..7bbd81d
--- /dev/null
+++ b/packages/Tethering/res/values-ka/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ტეტერინგი ან უსადენო ქსელი აქტიურია"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"შეეხეთ დასაყენებლად."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ტეტერინგი გათიშულია"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"დამატებითი ინფორმაციისთვის დაუკავშირდით თქვენს ადმინისტრატორს"</string>
+</resources>
diff --git a/packages/Tethering/res/values-kk/strings.xml b/packages/Tethering/res/values-kk/strings.xml
new file mode 100644
index 0000000..7fd87a1
--- /dev/null
+++ b/packages/Tethering/res/values-kk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Тетеринг немесе хотспот қосулы"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Реттеу үшін түртіңіз."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Тетеринг өшірілді"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Мәліметтерді әкімшіден алыңыз"</string>
+</resources>
diff --git a/packages/Tethering/res/values-km/strings.xml b/packages/Tethering/res/values-km/strings.xml
new file mode 100644
index 0000000..2f85224
--- /dev/null
+++ b/packages/Tethering/res/values-km/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬហតស្ពតសកម្ម"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ប៉ះដើម្បីកំណត់"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ការភ្ជាប់ត្រូវបានបិទ"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ទាក់ទងអ្នកគ្រប់គ្រងរបស់អ្នកសម្រាប់ព័ត៌មានលម្អិត"</string>
+</resources>
diff --git a/packages/Tethering/res/values-kn/strings.xml b/packages/Tethering/res/values-kn/strings.xml
new file mode 100644
index 0000000..f11a83ea
--- /dev/null
+++ b/packages/Tethering/res/values-kn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ಟೆಥರಿಂಗ್ ಅಥವಾ ಹಾಟ್ಸ್ಪಾಟ್ ಸಕ್ರಿಯವಾಗಿದೆ"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ಟೆಥರಿಂಗ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ವಿವರಗಳಿಗಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ko/strings.xml b/packages/Tethering/res/values-ko/strings.xml
new file mode 100644
index 0000000..57f24f5
--- /dev/null
+++ b/packages/Tethering/res/values-ko/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"테더링 또는 핫스팟 사용"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"설정하려면 탭하세요."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"테더링이 사용 중지됨"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"자세한 정보는 관리자에게 문의하세요."</string>
+</resources>
diff --git a/packages/Tethering/res/values-ky/strings.xml b/packages/Tethering/res/values-ky/strings.xml
new file mode 100644
index 0000000..7985485
--- /dev/null
+++ b/packages/Tethering/res/values-ky/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Жалгаштыруу же хотспот жандырылган"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Жөндөө үчүн таптап коюңуз."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Жалгаштыруу функциясы өчүрүлгөн"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Кеңири маалымат үчүн администраторуңузга кайрылыңыз"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lo/strings.xml b/packages/Tethering/res/values-lo/strings.xml
new file mode 100644
index 0000000..78f1585
--- /dev/null
+++ b/packages/Tethering/res/values-lo/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ເປີດການປ່ອຍສັນຍານ ຫຼືຮັອດສະປອດແລ້ວ"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ແຕະເພື່ອຕັ້ງຄ່າ."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ການປ່ອຍສັນຍານຖືກປິດໄວ້"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບລາຍລະອຽດ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lt/strings.xml b/packages/Tethering/res/values-lt/strings.xml
new file mode 100644
index 0000000..ebff8ac
--- /dev/null
+++ b/packages/Tethering/res/values-lt/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Susietas ar aktyvus"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Palieskite, kad nustatytumėte."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Įrenginio kaip modemo naudojimas išjungtas"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Jei reikia išsamios informacijos, susisiekite su administratoriumi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-lv/strings.xml b/packages/Tethering/res/values-lv/strings.xml
new file mode 100644
index 0000000..54d0048
--- /dev/null
+++ b/packages/Tethering/res/values-lv/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Piesaiste vai tīklājs ir aktīvs."</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Pieskarieties, lai iestatītu."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Piesaiste ir atspējota"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Lai iegūtu detalizētu informāciju, sazinieties ar savu administratoru."</string>
+</resources>
diff --git a/packages/Tethering/res/values-mk/strings.xml b/packages/Tethering/res/values-mk/strings.xml
new file mode 100644
index 0000000..0fab8aa
--- /dev/null
+++ b/packages/Tethering/res/values-mk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Поврзувањето или точката на пристап се активни"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Допрете за поставување."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Врзувањето е оневозможено"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Контактирајте со администраторот за детали"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ml/strings.xml b/packages/Tethering/res/values-ml/strings.xml
new file mode 100644
index 0000000..fd7e556
--- /dev/null
+++ b/packages/Tethering/res/values-ml/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ടെതറിംഗ് അല്ലെങ്കിൽ ഹോട്ട്സ്പോട്ട് സജീവമാണ്"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"സജ്ജമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ടെതറിംഗ് പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"വിശദവിവരങ്ങൾക്ക് നിങ്ങളുടെ അഡ്മിനെ ബന്ധപ്പെടുക"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mn/strings.xml b/packages/Tethering/res/values-mn/strings.xml
new file mode 100644
index 0000000..4596577
--- /dev/null
+++ b/packages/Tethering/res/values-mn/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Модем болгох эсвэл идэвхтэй цэг болгох"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Тохируулахын тулд товшино уу."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Модем болгох боломжгүй байна"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Дэлгэрэнгүй мэдээлэл авахын тулд админтайгаа холбогдоно уу"</string>
+</resources>
diff --git a/packages/Tethering/res/values-mr/strings.xml b/packages/Tethering/res/values-mr/strings.xml
new file mode 100644
index 0000000..85c9ade
--- /dev/null
+++ b/packages/Tethering/res/values-mr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"टेदरिंग किंवा हॉटस्पॉट सक्रिय"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"सेट करण्यासाठी टॅप करा."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिंग बंद आहे"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"तपशीलांसाठी तुमच्या प्रशासकाशी संपर्क साधा"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ms/strings.xml b/packages/Tethering/res/values-ms/strings.xml
new file mode 100644
index 0000000..ec6bdbd
--- /dev/null
+++ b/packages/Tethering/res/values-ms/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Penambatan atau titik panas aktif"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Ketik untuk membuat persediaan."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Penambatan dilumpuhkan"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hubungi pentadbir anda untuk maklumat lanjut"</string>
+</resources>
diff --git a/packages/Tethering/res/values-my/strings.xml b/packages/Tethering/res/values-my/strings.xml
new file mode 100644
index 0000000..83978b6
--- /dev/null
+++ b/packages/Tethering/res/values-my/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"တဆင့်ပြန်လည်လွှင့်ခြင်း သို့မဟုတ် ဟော့စပေါ့ ဖွင့်ထားသည်"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"စနစ်ထည့်သွင်းရန် တို့ပါ။"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"မိုဘိုင်းဖုန်းကို မိုဒမ်အဖြစ်သုံးခြင်းအား ပိတ်ထားသည်"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"အသေးစိတ်အချက်အလက်များအတွက် သင့်စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-nb/strings.xml b/packages/Tethering/res/values-nb/strings.xml
new file mode 100644
index 0000000..9abf32d
--- /dev/null
+++ b/packages/Tethering/res/values-nb/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Internettdeling eller trådløs sone er aktiv"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Trykk for å konfigurere."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internettdeling er slått av"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ta kontakt med administratoren din for å få mer informasjon"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ne/strings.xml b/packages/Tethering/res/values-ne/strings.xml
new file mode 100644
index 0000000..c886929
--- /dev/null
+++ b/packages/Tethering/res/values-ne/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"टेथर गर्ने वा हटस्पट सक्रिय"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"सेटअप गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"टेदरिङलाई असक्षम पारिएको छ"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"विवरणहरूका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्"</string>
+</resources>
diff --git a/packages/Tethering/res/values-nl/strings.xml b/packages/Tethering/res/values-nl/strings.xml
new file mode 100644
index 0000000..0ec4bff
--- /dev/null
+++ b/packages/Tethering/res/values-nl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering of hotspot actief"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tik om in te stellen."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering is uitgeschakeld"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Neem contact op met je beheerder voor meer informatie"</string>
+</resources>
diff --git a/packages/Tethering/res/values-or/strings.xml b/packages/Tethering/res/values-or/strings.xml
new file mode 100644
index 0000000..4576857
--- /dev/null
+++ b/packages/Tethering/res/values-or/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ଟିଥରିଙ୍ଗ କିମ୍ୱା ହଟସ୍ପଟ୍ ସକ୍ରିୟ ଅଛି"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ସେଟଅପ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ଟିଥରିଙ୍ଗ ଅକ୍ଷମ କରାଯାଇଛି"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ବିବରଣୀ ପାଇଁ ନିଜ ଆଡମିନ୍ଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pa/strings.xml b/packages/Tethering/res/values-pa/strings.xml
new file mode 100644
index 0000000..deddf2e
--- /dev/null
+++ b/packages/Tethering/res/values-pa/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ਟੈਦਰਿੰਗ ਜਾਂ ਹੌਟਸਪੌਟ ਕਿਰਿਆਸ਼ੀਲ"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"ਸਥਾਪਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ਟੈਦਰਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ਵੇਰਵਿਆਂ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਸੰਪਰਕ ਕਰੋ"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pl/strings.xml b/packages/Tethering/res/values-pl/strings.xml
new file mode 100644
index 0000000..48d8468
--- /dev/null
+++ b/packages/Tethering/res/values-pl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Aktywny tethering lub punkt dostępu"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Kliknij, by skonfigurować."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering został wyłączony"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Aby uzyskać szczegółowe informacje, skontaktuj się z administratorem"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt-rBR/strings.xml b/packages/Tethering/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..32c22b8
--- /dev/null
+++ b/packages/Tethering/res/values-pt-rBR/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt-rPT/strings.xml b/packages/Tethering/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..641e22f
--- /dev/null
+++ b/packages/Tethering/res/values-pt-rPT/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ligação ponto a ponto ou hotspot activos"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"A ligação (à Internet) via telemóvel está desativada."</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contacte o gestor para obter detalhes."</string>
+</resources>
diff --git a/packages/Tethering/res/values-pt/strings.xml b/packages/Tethering/res/values-pt/strings.xml
new file mode 100644
index 0000000..32c22b8
--- /dev/null
+++ b/packages/Tethering/res/values-pt/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ponto de acesso ou tethering ativo"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Toque para configurar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering desativado"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Fale com seu administrador para saber detalhes"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ro/strings.xml b/packages/Tethering/res/values-ro/strings.xml
new file mode 100644
index 0000000..f861f73
--- /dev/null
+++ b/packages/Tethering/res/values-ro/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering sau hotspot activ"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Atingeți ca să configurați."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tetheringul este dezactivat"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Contactați administratorul pentru detalii"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ru/strings.xml b/packages/Tethering/res/values-ru/strings.xml
new file mode 100644
index 0000000..027cb41
--- /dev/null
+++ b/packages/Tethering/res/values-ru/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Включен режим модема"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Нажмите, чтобы настроить."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Включить режим модема нельзя"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Обратитесь к администратору, чтобы узнать подробности."</string>
+</resources>
diff --git a/packages/Tethering/res/values-si/strings.xml b/packages/Tethering/res/values-si/strings.xml
new file mode 100644
index 0000000..7d8599f
--- /dev/null
+++ b/packages/Tethering/res/values-si/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ටෙදරින් හෝ හොට්ස්පොට් සක්රීයයි"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"පිහිටුවීමට තට්ටු කරන්න."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ටෙදරින් අබල කර ඇත"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"විස්තර සඳහා ඔබගේ පරිපාලක අමතන්න"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sk/strings.xml b/packages/Tethering/res/values-sk/strings.xml
new file mode 100644
index 0000000..a8fe297
--- /dev/null
+++ b/packages/Tethering/res/values-sk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering alebo prístupový bod je aktívny"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Klepnutím prejdete na nastavenie."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering je deaktivovaný"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"O podrobnosti požiadajte svojho správcu"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sl/strings.xml b/packages/Tethering/res/values-sl/strings.xml
new file mode 100644
index 0000000..b5e5e38
--- /dev/null
+++ b/packages/Tethering/res/values-sl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Aktivna povezava z internetom ali dostopna točka sta aktivni"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Dotaknite se, če želite nastaviti."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Povezava z internetom prek mobilnega telefona je onemogočena"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Za podrobnosti se obrnite na skrbnika"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sq/strings.xml b/packages/Tethering/res/values-sq/strings.xml
new file mode 100644
index 0000000..fdd4906
--- /dev/null
+++ b/packages/Tethering/res/values-sq/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Lidhja e çiftimit ose ajo e qasjes në zona publike interneti është aktive"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Trokit për ta konfiguruar."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Lidhja e çiftimit është çaktivizuar"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakto me administratorin për detaje"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sr/strings.xml b/packages/Tethering/res/values-sr/strings.xml
new file mode 100644
index 0000000..9fab34589
--- /dev/null
+++ b/packages/Tethering/res/values-sr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Активно повезивање са интернетом преко мобилног уређаја или хотспот"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Додирните да бисте подесили."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Привезивање је онемогућено"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Потражите детаље од администратора"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sv/strings.xml b/packages/Tethering/res/values-sv/strings.xml
new file mode 100644
index 0000000..10eeb0f
--- /dev/null
+++ b/packages/Tethering/res/values-sv/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Internetdelning eller surfzon aktiverad"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Tryck om du vill konfigurera."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Internetdelning har inaktiverats"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Kontakta administratören om du vill veta mer"</string>
+</resources>
diff --git a/packages/Tethering/res/values-sw/strings.xml b/packages/Tethering/res/values-sw/strings.xml
new file mode 100644
index 0000000..3353963
--- /dev/null
+++ b/packages/Tethering/res/values-sw/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Kushiriki au kusambaza intaneti kumewashwa"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Gusa ili uweke mipangilio."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Umezima kipengele cha kusambaza mtandao"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Wasiliana na msimamizi wako ili upate maelezo zaidi"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ta/strings.xml b/packages/Tethering/res/values-ta/strings.xml
new file mode 100644
index 0000000..b1e5cc2
--- /dev/null
+++ b/packages/Tethering/res/values-ta/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"டெதெரிங்/ஹாட்ஸ்பாட் இயங்குகிறது"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"அமைக்க, தட்டவும்."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"இணைப்பு முறை முடக்கப்பட்டுள்ளது"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"விவரங்களுக்கு, உங்கள் நிர்வாகியைத் தொடர்புகொள்ளவும்"</string>
+</resources>
diff --git a/packages/Tethering/res/values-te/strings.xml b/packages/Tethering/res/values-te/strings.xml
new file mode 100644
index 0000000..aae40de
--- /dev/null
+++ b/packages/Tethering/res/values-te/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"టీథర్ చేయబడినది లేదా హాట్స్పాట్ సక్రియంగా ఉండేది"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"సెటప్ చేయడానికి నొక్కండి."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"టెథెరింగ్ నిలిపివేయబడింది"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"వివరాల కోసం మీ నిర్వాహకులను సంప్రదించండి"</string>
+</resources>
diff --git a/packages/Tethering/res/values-th/strings.xml b/packages/Tethering/res/values-th/strings.xml
new file mode 100644
index 0000000..1b80056
--- /dev/null
+++ b/packages/Tethering/res/values-th/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"การปล่อยสัญญาณหรือฮอตสปอตทำงานอยู่"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"แตะเพื่อตั้งค่า"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ปิดใช้การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือแล้ว"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"ติดต่อผู้ดูแลระบบเพื่อขอรายละเอียด"</string>
+</resources>
diff --git a/packages/Tethering/res/values-tl/strings.xml b/packages/Tethering/res/values-tl/strings.xml
new file mode 100644
index 0000000..12863f9
--- /dev/null
+++ b/packages/Tethering/res/values-tl/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Pagsasama o aktibong hotspot"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"I-tap upang i-set up."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Naka-disable ang pag-tether"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Makipag-ugnayan sa iyong admin para sa mga detalye"</string>
+</resources>
diff --git a/packages/Tethering/res/values-tr/strings.xml b/packages/Tethering/res/values-tr/strings.xml
new file mode 100644
index 0000000..bfcf1ac
--- /dev/null
+++ b/packages/Tethering/res/values-tr/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Tethering veya hotspot etkin"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Ayarlamak için dokunun."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Tethering devre dışı bırakıldı"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Ayrıntılı bilgi için yöneticinize başvurun"</string>
+</resources>
diff --git a/packages/Tethering/res/values-uk/strings.xml b/packages/Tethering/res/values-uk/strings.xml
new file mode 100644
index 0000000..8e159c07
--- /dev/null
+++ b/packages/Tethering/res/values-uk/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Прив\'язка чи точка дост. активна"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Торкніться, щоб налаштувати."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Використання телефона в режимі модема вимкнено"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Щоб дізнатися більше, зв’яжіться з адміністратором"</string>
+</resources>
diff --git a/packages/Tethering/res/values-ur/strings.xml b/packages/Tethering/res/values-ur/strings.xml
new file mode 100644
index 0000000..89195d4
--- /dev/null
+++ b/packages/Tethering/res/values-ur/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"ٹیدرنگ یا ہاٹ اسپاٹ فعال"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"سیٹ اپ کرنے کیلئے تھپتھپائیں۔"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"ٹیدرنگ غیر فعال ہے"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"تفصیلات کے لئے اپنے منتظم سے رابطہ کریں"</string>
+</resources>
diff --git a/packages/Tethering/res/values-uz/strings.xml b/packages/Tethering/res/values-uz/strings.xml
new file mode 100644
index 0000000..0ac4d4a
--- /dev/null
+++ b/packages/Tethering/res/values-uz/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Modem rejimi yoniq"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Sozlash uchun bosing."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Modem rejimi faolsizlantirildi"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Tafsilotlari uchun administratoringizga murojaat qiling"</string>
+</resources>
diff --git a/packages/Tethering/res/values-vi/strings.xml b/packages/Tethering/res/values-vi/strings.xml
new file mode 100644
index 0000000..85a4db8
--- /dev/null
+++ b/packages/Tethering/res/values-vi/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Chức năng điểm truy cập Internet hoặc điểm phát sóng đang hoạt động"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Nhấn để thiết lập."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Đã tắt tính năng chia sẻ kết nối"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Hãy liên hệ với quản trị viên của bạn để biết chi tiết"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rCN/strings.xml b/packages/Tethering/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..ff1fe03
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rCN/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"网络共享或热点已启用"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"点按即可进行设置。"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"网络共享已停用"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"请与您的管理员联系以了解详情"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rHK/strings.xml b/packages/Tethering/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..0de39fa
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rHK/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"已啟用網絡共享或熱點"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"輕按即可設定。"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"網絡共享已停用"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"請聯絡您的管理員以瞭解詳情"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zh-rTW/strings.xml b/packages/Tethering/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9a117bb
--- /dev/null
+++ b/packages/Tethering/res/values-zh-rTW/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"網路共用或無線基地台已啟用"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"輕觸即可進行設定。"</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"數據連線已停用"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"詳情請洽你的管理員"</string>
+</resources>
diff --git a/packages/Tethering/res/values-zu/strings.xml b/packages/Tethering/res/values-zu/strings.xml
new file mode 100644
index 0000000..8fe10d8
--- /dev/null
+++ b/packages/Tethering/res/values-zu/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="tethered_notification_title" msgid="3146694234398202601">"Ukusebenzisa njengemodemu noma i-hotspot ephathekayo kuvuliwe"</string>
+ <string name="tethered_notification_message" msgid="2113628520792055377">"Thepha ukuze usethe."</string>
+ <string name="disable_tether_notification_title" msgid="7526977944111313195">"Ukusebenzisa ifoni njengemodemu kukhutshaziwe"</string>
+ <string name="disable_tether_notification_message" msgid="2913366428516852495">"Xhumana nomphathi wakho ukuze uthole imininingwane"</string>
+</resources>
diff --git a/packages/Tethering/res/values/strings.xml b/packages/Tethering/res/values/strings.xml
new file mode 100644
index 0000000..ca866a9
--- /dev/null
+++ b/packages/Tethering/res/values/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Shown when the device is tethered -->
+ <!-- Strings for tethered notification title [CHAR LIMIT=200] -->
+ <string name="tethered_notification_title">Tethering or hotspot active</string>
+ <!-- Strings for tethered notification message [CHAR LIMIT=200] -->
+ <string name="tethered_notification_message">Tap to set up.</string>
+
+ <!-- This notification is shown when tethering has been disabled on a user's device.
+ The device is managed by the user's employer. Tethering can't be turned on unless the
+ IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
+ <!-- Strings for tether disabling notification title [CHAR LIMIT=200] -->
+ <string name="disable_tether_notification_title">Tethering is disabled</string>
+ <!-- Strings for tether disabling notification message [CHAR LIMIT=200] -->
+ <string name="disable_tether_notification_message">Contact your admin for details</string>
+</resources>
\ No newline at end of file
diff --git a/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
new file mode 100644
index 0000000..3218c0b
--- /dev/null
+++ b/packages/Tethering/src/android/net/util/BaseNetdUnsolicitedEventListener.java
@@ -0,0 +1,70 @@
+/*
+ * 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.net.util;
+
+import android.net.INetdUnsolicitedEventListener;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Base {@link INetdUnsolicitedEventListener} that provides no-op implementations which can be
+ * overridden.
+ */
+public class BaseNetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
+
+ @Override
+ public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel, long timestampNs,
+ int uid) { }
+
+ @Override
+ public void onQuotaLimitReached(@NonNull String alertName, @NonNull String ifName) { }
+
+ @Override
+ public void onInterfaceDnsServerInfo(@NonNull String ifName, long lifetimeS,
+ @NonNull String[] servers) { }
+
+ @Override
+ public void onInterfaceAddressUpdated(@NonNull String addr, String ifName, int flags,
+ int scope) { }
+
+ @Override
+ public void onInterfaceAddressRemoved(@NonNull String addr, @NonNull String ifName, int flags,
+ int scope) { }
+
+ @Override
+ public void onInterfaceAdded(@NonNull String ifName) { }
+
+ @Override
+ public void onInterfaceRemoved(@NonNull String ifName) { }
+
+ @Override
+ public void onInterfaceChanged(@NonNull String ifName, boolean up) { }
+
+ @Override
+ public void onInterfaceLinkStateChanged(@NonNull String ifName, boolean up) { }
+
+ @Override
+ public void onRouteChanged(boolean updated, @NonNull String route, @NonNull String gateway,
+ @NonNull String ifName) { }
+
+ @Override
+ public void onStrictCleartextDetected(int uid, @NonNull String hex) { }
+
+ @Override
+ public int getInterfaceVersion() {
+ return INetdUnsolicitedEventListener.VERSION;
+ }
+}
diff --git a/services/net/java/android/net/util/VersionedBroadcastListener.java b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
similarity index 91%
rename from services/net/java/android/net/util/VersionedBroadcastListener.java
rename to packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
index 107c404..e2804ab 100644
--- a/services/net/java/android/net/util/VersionedBroadcastListener.java
+++ b/packages/Tethering/src/android/net/util/VersionedBroadcastListener.java
@@ -39,10 +39,6 @@
public class VersionedBroadcastListener {
private static final boolean DBG = false;
- public interface IntentCallback {
- public void run(Intent intent);
- }
-
private final String mTag;
private final Context mContext;
private final Handler mHandler;
@@ -61,6 +57,7 @@
mGenerationNumber = new AtomicInteger(0);
}
+ /** Start listening to intent broadcast. */
public void startListening() {
if (DBG) Log.d(mTag, "startListening");
if (mReceiver != null) return;
@@ -69,6 +66,7 @@
mContext.registerReceiver(mReceiver, mFilter, null, mHandler);
}
+ /** Stop listening to intent broadcast. */
public void stopListening() {
if (DBG) Log.d(mTag, "stopListening");
if (mReceiver == null) return;
@@ -85,8 +83,7 @@
// Used to verify this receiver is still current.
public final int generationNumber;
- public Receiver(
- String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
+ Receiver(String tag, AtomicInteger atomicGenerationNumber, Consumer<Intent> callback) {
this.tag = tag;
this.atomicGenerationNumber = atomicGenerationNumber;
this.callback = callback;
@@ -98,8 +95,8 @@
final int currentGenerationNumber = atomicGenerationNumber.get();
if (DBG) {
- Log.d(tag, "receiver generationNumber=" + generationNumber +
- ", current generationNumber=" + currentGenerationNumber);
+ Log.d(tag, "receiver generationNumber=" + generationNumber
+ + ", current generationNumber=" + currentGenerationNumber);
}
if (generationNumber != currentGenerationNumber) return;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 6b0f1de..ba5d08d 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -47,6 +47,7 @@
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -55,7 +56,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
-import com.android.server.connectivity.MockableSystemProperties;
import java.io.PrintWriter;
@@ -94,7 +94,6 @@
private final ArraySet<Integer> mCurrentTethers;
private final Context mContext;
private final int mPermissionChangeMessageCode;
- private final MockableSystemProperties mSystemProperties;
private final SharedLog mLog;
private final SparseIntArray mEntitlementCacheValue;
private final EntitlementHandler mHandler;
@@ -110,12 +109,12 @@
private TetheringConfigurationFetcher mFetcher;
public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
- int permissionChangeMessageCode, MockableSystemProperties systemProperties) {
+ int permissionChangeMessageCode) {
+
mContext = ctx;
mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
mCellularPermitted = new SparseIntArray();
- mSystemProperties = systemProperties;
mEntitlementCacheValue = new SparseIntArray();
mTetherMasterSM = tetherMasterSM;
mPermissionChangeMessageCode = permissionChangeMessageCode;
@@ -287,7 +286,7 @@
*/
@VisibleForTesting
protected boolean isTetherProvisioningRequired(final TetheringConfiguration config) {
- if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
+ if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
|| config.provisioningApp.length == 0) {
return false;
}
@@ -526,8 +525,8 @@
handleMaybeRunProvisioning(config);
break;
case EVENT_GET_ENTITLEMENT_VALUE:
- handleGetLatestTetheringEntitlementValue(msg.arg1, (ResultReceiver) msg.obj,
- toBool(msg.arg2));
+ handleRequestLatestTetheringEntitlementValue(msg.arg1,
+ (ResultReceiver) msg.obj, toBool(msg.arg2));
break;
default:
mLog.log("Unknown event: " + msg.what);
@@ -651,15 +650,15 @@
}
/** Get the last value of the tethering entitlement check. */
- public void getLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
+ public void requestLatestTetheringEntitlementResult(int downstream, ResultReceiver receiver,
boolean showEntitlementUi) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_GET_ENTITLEMENT_VALUE,
downstream, encodeBool(showEntitlementUi), receiver));
}
- private void handleGetLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
- boolean showEntitlementUi) {
+ private void handleRequestLatestTetheringEntitlementValue(int downstream,
+ ResultReceiver receiver, boolean showEntitlementUi) {
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
if (!isTetherProvisioningRequired(config)) {
receiver.send(TETHER_ERROR_NO_ERROR, null);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
similarity index 92%
rename from services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 8186343..edfe3ca 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -82,6 +82,7 @@
mNextSubnetId = 0;
}
+ /** Add active downstream to ipv6 tethering candidate list. */
public void addActiveDownstream(IpServer downstream, int mode) {
if (findDownstream(downstream) == null) {
// Adding a new downstream appends it to the list. Adding a
@@ -97,6 +98,7 @@
}
}
+ /** Remove downstream from ipv6 tethering candidate list. */
public void removeActiveDownstream(IpServer downstream) {
stopIPv6TetheringOn(downstream);
if (mActiveDownstreams.remove(findDownstream(downstream))) {
@@ -112,6 +114,11 @@
}
}
+ /**
+ * Call when upstream NetworkState may be changed.
+ * If upstream has ipv6 for tethering, update this new NetworkState
+ * to IpServer. Otherwise stop ipv6 tethering on downstream interfaces.
+ */
public void updateUpstreamNetworkState(NetworkState ns) {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
@@ -122,8 +129,8 @@
return;
}
- if (mUpstreamNetworkState != null &&
- !ns.network.equals(mUpstreamNetworkState.network)) {
+ if (mUpstreamNetworkState != null
+ && !ns.network.equals(mUpstreamNetworkState.network)) {
stopIPv6TetheringOnAllInterfaces();
}
@@ -221,8 +228,8 @@
for (RouteInfo routeInfo : lp.getRoutes()) {
final IpPrefix destination = routeInfo.getDestination();
- if ((destination.getAddress() instanceof Inet6Address) &&
- (destination.getPrefixLength() <= 64)) {
+ if ((destination.getAddress() instanceof Inet6Address)
+ && (destination.getPrefixLength() <= 64)) {
v6only.addRoute(routeInfo);
}
}
@@ -242,12 +249,12 @@
// TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
// announce our own IPv6 address as DNS server.
private static boolean isIPv6GlobalAddress(InetAddress ip) {
- return (ip instanceof Inet6Address) &&
- !ip.isAnyLocalAddress() &&
- !ip.isLoopbackAddress() &&
- !ip.isLinkLocalAddress() &&
- !ip.isSiteLocalAddress() &&
- !ip.isMulticastAddress();
+ return (ip instanceof Inet6Address)
+ && !ip.isAnyLocalAddress()
+ && !ip.isLoopbackAddress()
+ && !ip.isLinkLocalAddress()
+ && !ip.isSiteLocalAddress()
+ && !ip.isMulticastAddress();
}
private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 01339a4..00a6773 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -107,6 +107,8 @@
public OffloadHardwareInterface(Handler h, SharedLog log) {
mHandler = h;
mLog = log.forSubComponent(TAG);
+
+ System.loadLibrary("tetheroffloadjni");
}
/** Get default value indicating whether offload is supported. */
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
similarity index 88%
rename from services/core/java/com/android/server/connectivity/Tethering.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index acedc36..7c78ef8 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package com.android.server.connectivity.tethering;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -47,8 +47,6 @@
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.server.ConnectivityService.SHORT_ARG;
-
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -62,9 +60,10 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
+import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.ITetheringEventCallback;
+import android.net.ITetherInternalCallback;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -72,7 +71,10 @@
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkUtils;
+import android.net.TetherStatesParcel;
+import android.net.TetheringConfigurationParcel;
import android.net.ip.IpServer;
+import android.net.util.BaseNetdUnsolicitedEventListener;
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
@@ -87,13 +89,10 @@
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.UserManagerInternal;
-import android.os.UserManagerInternal.UserRestrictionsListener;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -110,15 +109,7 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
-import com.android.server.LocalServices;
-import com.android.server.connectivity.tethering.EntitlementManager;
-import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
-import com.android.server.connectivity.tethering.OffloadController;
-import com.android.server.connectivity.tethering.TetheringConfiguration;
-import com.android.server.connectivity.tethering.TetheringDependencies;
-import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
-import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
-import com.android.server.net.BaseNetworkObserver;
+import com.android.tethering.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -127,32 +118,32 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
/**
- * @hide
*
* This class holds much of the business logic to allow Android devices
* to act as IP gateways via USB, BT, and WiFi interfaces.
*/
-public class Tethering extends BaseNetworkObserver {
+public class Tethering {
- private final static String TAG = Tethering.class.getSimpleName();
- private final static boolean DBG = false;
- private final static boolean VDBG = false;
+ private static final String TAG = Tethering.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
- private static final Class[] messageClasses = {
+ private static final Class[] sMessageClasses = {
Tethering.class, TetherMasterSM.class, IpServer.class
};
private static final SparseArray<String> sMagicDecoderRing =
- MessageUtils.findMessageNames(messageClasses);
+ MessageUtils.findMessageNames(sMessageClasses);
private static class TetherState {
public final IpServer ipServer;
public int lastState;
public int lastError;
- public TetherState(IpServer ipServer) {
+ TetherState(IpServer ipServer) {
this.ipServer = ipServer;
// Assume all state machines start out available and with no errors.
lastState = IpServer.STATE_AVAILABLE;
@@ -177,6 +168,7 @@
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
private final BroadcastReceiver mStateReceiver;
+ // Stopship: replace mNMService before production.
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
@@ -191,10 +183,13 @@
private final TetheringDependencies mDeps;
private final EntitlementManager mEntitlementMgr;
private final Handler mHandler;
- private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
- new RemoteCallbackList<>();
private final PhoneStateListener mPhoneStateListener;
+ private final INetd mNetd;
+ private final NetdCallback mNetdCallback;
+ private final UserRestrictionActionListener mTetheringRestriction;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
+ // All the usage of mTetherInternalCallback should run in the same thread.
+ private ITetherInternalCallback mTetherInternalCallback = null;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -205,18 +200,17 @@
// True iff. WiFi tethering should be started when soft AP is ready.
private boolean mWifiTetherRequested;
private Network mTetherUpstream;
+ private TetherStatesParcel mTetherStatesParcel;
- public Tethering(Context context, INetworkManagementService nmService,
- INetworkStatsService statsService, INetworkPolicyManager policyManager,
- Looper looper, MockableSystemProperties systemProperties,
- TetheringDependencies deps) {
- mLog.mark("constructed");
- mContext = context;
- mNMService = nmService;
- mStatsService = statsService;
- mPolicyManager = policyManager;
- mLooper = looper;
+ public Tethering(TetheringDependencies deps) {
+ mLog.mark("Tethering.constructed");
mDeps = deps;
+ mContext = mDeps.getContext();
+ mNMService = mDeps.getINetworkManagementService();
+ mStatsService = mDeps.getINetworkStatsService();
+ mPolicyManager = mDeps.getINetworkPolicyManager();
+ mNetd = mDeps.getINetd(mContext);
+ mLooper = mDeps.getTetheringLooper();
mPublicSync = new Object();
@@ -239,7 +233,7 @@
// EntitlementManager will send EVENT_UPSTREAM_PERMISSION_CHANGED when cellular upstream
// permission is changed according to entitlement check result.
mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM, mLog,
- TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED, systemProperties);
+ TetherMasterSM.EVENT_UPSTREAM_PERMISSION_CHANGED);
mEntitlementMgr.setOnUiEntitlementFailedListener((int downstream) -> {
mLog.log("OBSERVED UiEnitlementFailed");
stopTethering(downstream);
@@ -278,10 +272,22 @@
mStateReceiver = new StateReceiver();
+ mNetdCallback = new NetdCallback();
+ try {
+ mNetd.registerUnsolicitedEventListener(mNetdCallback);
+ } catch (RemoteException e) {
+ mLog.e("Unable to register netd UnsolicitedEventListener");
+ }
+
+ final UserManager userManager = (UserManager) mContext.getSystemService(
+ Context.USER_SERVICE);
+ mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+
// Load tethering configuration.
updateConfiguration();
startStateMachineUpdaters(mHandler);
+ startTrackDefaultNetwork();
}
private void startStateMachineUpdaters(Handler handler) {
@@ -295,6 +301,7 @@
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
@@ -303,11 +310,6 @@
filter.addDataScheme("file");
mContext.registerReceiver(mStateReceiver, filter, null, handler);
- final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
- // This check is useful only for some unit tests; example: ConnectivityServiceTest.
- if (umi != null) {
- umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
- }
}
private WifiManager getWifiManager() {
@@ -318,6 +320,7 @@
private void updateConfiguration() {
mConfig = mDeps.generateTetheringConfiguration(mContext, mLog, mActiveDataSubId);
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
+ reportConfigurationChanged(mConfig.toStableParcelable());
}
private void maybeDunSettingChanged() {
@@ -327,10 +330,31 @@
updateConfiguration();
}
- @Override
- public void interfaceStatusChanged(String iface, boolean up) {
+ private class NetdCallback extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onInterfaceChanged(String ifName, boolean up) {
+ mHandler.post(() -> interfaceStatusChanged(ifName, up));
+ }
+
+ @Override
+ public void onInterfaceLinkStateChanged(String ifName, boolean up) {
+ mHandler.post(() -> interfaceLinkStateChanged(ifName, up));
+ }
+
+ @Override
+ public void onInterfaceAdded(String ifName) {
+ mHandler.post(() -> interfaceAdded(ifName));
+ }
+
+ @Override
+ public void onInterfaceRemoved(String ifName) {
+ mHandler.post(() -> interfaceRemoved(ifName));
+ }
+ }
+
+ void interfaceStatusChanged(String iface, boolean up) {
// Never called directly: only called from interfaceLinkStateChanged.
- // See NetlinkHandler.cpp:71.
+ // See NetlinkHandler.cpp: notifyInterfaceChanged.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
synchronized (mPublicSync) {
if (up) {
@@ -349,8 +373,7 @@
}
}
- @Override
- public void interfaceLinkStateChanged(String iface, boolean up) {
+ void interfaceLinkStateChanged(String iface, boolean up) {
interfaceStatusChanged(iface, up);
}
@@ -369,28 +392,27 @@
return TETHERING_INVALID;
}
- @Override
- public void interfaceAdded(String iface) {
+ void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
synchronized (mPublicSync) {
maybeTrackNewInterfaceLocked(iface);
}
}
- @Override
- public void interfaceRemoved(String iface) {
+
+ void interfaceRemoved(String iface) {
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
synchronized (mPublicSync) {
stopTrackingInterfaceLocked(iface);
}
}
- public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
+ void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi);
enableTetheringInternal(type, true /* enabled */, receiver);
}
- public void stopTethering(int type) {
+ void stopTethering(int type) {
enableTetheringInternal(type, false /* disabled */, null);
mEntitlementMgr.stopProvisioningIfNeeded(type);
}
@@ -434,8 +456,8 @@
mLog.e("setWifiTethering: failed to get WifiManager!");
return TETHER_ERROR_SERVICE_UNAVAIL;
}
- if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
- (!enable && mgr.stopSoftAp())) {
+ if ((enable && mgr.startSoftAp(null /* use existing wifi config */))
+ || (!enable && mgr.stopSoftAp())) {
mWifiTetherRequested = enable;
return TETHER_ERROR_NO_ERROR;
}
@@ -450,8 +472,8 @@
private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null || !adapter.isEnabled()) {
- Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
- (adapter == null));
+ Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ + (adapter == null));
sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL);
return;
}
@@ -487,7 +509,7 @@
}, BluetoothProfile.PAN);
}
- public int tether(String iface) {
+ int tether(String iface) {
return tether(iface, IpServer.STATE_TETHERED);
}
@@ -515,7 +537,7 @@
}
}
- public int untether(String iface) {
+ int untether(String iface) {
if (DBG) Log.d(TAG, "Untethering " + iface);
synchronized (mPublicSync) {
TetherState tetherState = mTetherStates.get(iface);
@@ -532,19 +554,19 @@
}
}
- public void untetherAll() {
+ void untetherAll() {
stopTethering(TETHERING_WIFI);
stopTethering(TETHERING_WIFI_P2P);
stopTethering(TETHERING_USB);
stopTethering(TETHERING_BLUETOOTH);
}
- public int getLastTetherError(String iface) {
+ int getLastTetherError(String iface) {
synchronized (mPublicSync) {
TetherState tetherState = mTetherStates.get(iface);
if (tetherState == null) {
- Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
- ", ignoring");
+ Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface
+ + ", ignoring");
return TETHER_ERROR_UNKNOWN_IFACE;
}
return tetherState.lastError;
@@ -559,12 +581,14 @@
final ArrayList<String> tetherList = new ArrayList<>();
final ArrayList<String> localOnlyList = new ArrayList<>();
final ArrayList<String> erroredList = new ArrayList<>();
+ final ArrayList<Integer> lastErrorList = new ArrayList<>();
boolean wifiTethered = false;
boolean usbTethered = false;
boolean bluetoothTethered = false;
final TetheringConfiguration cfg = mConfig;
+ final TetherStatesParcel mTetherStatesParcel = new TetherStatesParcel();
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
@@ -572,6 +596,7 @@
String iface = mTetherStates.keyAt(i);
if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
+ lastErrorList.add(tetherState.lastError);
} else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
availableList.add(iface);
} else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
@@ -588,9 +613,21 @@
}
}
}
+
+ mTetherStatesParcel.availableList = availableList.toArray(new String[0]);
+ mTetherStatesParcel.tetheredList = tetherList.toArray(new String[0]);
+ mTetherStatesParcel.localOnlyList = localOnlyList.toArray(new String[0]);
+ mTetherStatesParcel.erroredIfaceList = erroredList.toArray(new String[0]);
+ mTetherStatesParcel.lastErrorList = new int[lastErrorList.size()];
+ Iterator<Integer> iterator = lastErrorList.iterator();
+ for (int i = 0; i < lastErrorList.size(); i++) {
+ mTetherStatesParcel.lastErrorList[i] = iterator.next().intValue();
+ }
+ reportTetherStateChanged(mTetherStatesParcel);
+
final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
- bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
@@ -638,16 +675,16 @@
}
int icon = 0;
switch(id) {
- case SystemMessage.NOTE_TETHER_USB:
- icon = com.android.internal.R.drawable.stat_sys_tether_usb;
- break;
- case SystemMessage.NOTE_TETHER_BLUETOOTH:
- icon = com.android.internal.R.drawable.stat_sys_tether_bluetooth;
- break;
- case SystemMessage.NOTE_TETHER_GENERAL:
- default:
- icon = com.android.internal.R.drawable.stat_sys_tether_general;
- break;
+ case SystemMessage.NOTE_TETHER_USB:
+ icon = R.drawable.stat_sys_tether_usb;
+ break;
+ case SystemMessage.NOTE_TETHER_BLUETOOTH:
+ icon = R.drawable.stat_sys_tether_bluetooth;
+ break;
+ case SystemMessage.NOTE_TETHER_GENERAL:
+ default:
+ icon = R.drawable.stat_sys_tether_general;
+ break;
}
if (mLastNotificationId != 0) {
@@ -666,21 +703,21 @@
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0,
null, UserHandle.CURRENT);
- Resources r = Resources.getSystem();
+ Resources r = mContext.getResources();
final CharSequence title;
final CharSequence message;
if (tetheringOn) {
- title = r.getText(com.android.internal.R.string.tethered_notification_title);
- message = r.getText(com.android.internal.R.string.tethered_notification_message);
+ title = r.getText(R.string.tethered_notification_title);
+ message = r.getText(R.string.tethered_notification_message);
} else {
- title = r.getText(com.android.internal.R.string.disable_tether_notification_title);
- message = r.getText(com.android.internal.R.string.disable_tether_notification_message);
+ title = r.getText(R.string.disable_tether_notification_title);
+ message = r.getText(R.string.disable_tether_notification_message);
}
if (mTetheredNotificationBuilder == null) {
- mTetheredNotificationBuilder =
- new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS);
+ mTetheredNotificationBuilder = new Notification.Builder(mContext,
+ SystemNotificationChannels.NETWORK_STATUS);
mTetheredNotificationBuilder.setWhen(0)
.setOngoing(true)
.setColor(mContext.getColor(
@@ -701,7 +738,7 @@
@VisibleForTesting
protected void clearTetheredNotification() {
NotificationManager notificationManager =
- (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mLastNotificationId != 0) {
notificationManager.cancelAsUser(null, mLastNotificationId,
UserHandle.ALL);
@@ -726,14 +763,17 @@
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
mLog.log("OBSERVED configuration changed");
updateConfiguration();
+ } else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
+ mLog.log("OBSERVED user restrictions changed");
+ handleUserRestrictionAction();
}
}
private void handleConnectivityAction(Intent intent) {
final NetworkInfo networkInfo =
(NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO);
- if (networkInfo == null ||
- networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ if (networkInfo == null
+ || networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
return;
}
@@ -832,25 +872,35 @@
}
}
}
+
+ private void handleUserRestrictionAction() {
+ mTetheringRestriction.onUserRestrictionsChanged();
+ }
}
@VisibleForTesting
- protected static class TetheringUserRestrictionListener implements UserRestrictionsListener {
+ protected static class UserRestrictionActionListener {
+ private final UserManager mUserManager;
private final Tethering mWrapper;
+ public boolean mDisallowTethering;
- public TetheringUserRestrictionListener(Tethering wrapper) {
+ public UserRestrictionActionListener(UserManager um, Tethering wrapper) {
+ mUserManager = um;
mWrapper = wrapper;
+ mDisallowTethering = false;
}
- public void onUserRestrictionsChanged(int userId,
- Bundle newRestrictions,
- Bundle prevRestrictions) {
+ public void onUserRestrictionsChanged() {
+ // getUserRestrictions gets restriction for this process' user, which is the primary
+ // user. This is fine because DISALLOW_CONFIG_TETHERING can only be set on the primary
+ // user. See UserManager.DISALLOW_CONFIG_TETHERING.
+ final Bundle restrictions = mUserManager.getUserRestrictions();
final boolean newlyDisallowed =
- newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
- final boolean previouslyDisallowed =
- prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
- final boolean tetheringDisallowedChanged = (newlyDisallowed != previouslyDisallowed);
+ restrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
+ final boolean prevDisallowed = mDisallowTethering;
+ mDisallowTethering = newlyDisallowed;
+ final boolean tetheringDisallowedChanged = (newlyDisallowed != prevDisallowed);
if (!tetheringDisallowedChanged) {
return;
}
@@ -860,7 +910,7 @@
if (newlyDisallowed && isTetheringActiveOnDevice) {
mWrapper.showTetheredNotification(
- com.android.internal.R.drawable.stat_sys_tether_general, false);
+ R.drawable.stat_sys_tether_general, false);
mWrapper.untetherAll();
}
}
@@ -888,8 +938,8 @@
}
}
- mLog.log("Error disabling Wi-Fi IP serving; " +
- (TextUtils.isEmpty(ifname) ? "no interface name specified"
+ mLog.log("Error disabling Wi-Fi IP serving; "
+ + (TextUtils.isEmpty(ifname) ? "no interface name specified"
: "specified interface: " + ifname));
}
@@ -928,8 +978,8 @@
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
- "Cannot enable IP serving in mode %s on missing interface name",
- ipServingMode));
+ "Cannot enable IP serving in mode %s on missing interface name",
+ ipServingMode));
}
}
@@ -989,11 +1039,11 @@
}
}
- public TetheringConfiguration getTetheringConfiguration() {
+ TetheringConfiguration getTetheringConfiguration() {
return mConfig;
}
- public boolean hasTetherableConfiguration() {
+ boolean hasTetherableConfiguration() {
final TetheringConfiguration cfg = mConfig;
final boolean hasDownstreamConfiguration =
(cfg.tetherableUsbRegexs.length != 0)
@@ -1007,19 +1057,19 @@
// TODO - update callers to use getTetheringConfiguration(),
// which has only final members.
- public String[] getTetherableUsbRegexs() {
+ String[] getTetherableUsbRegexs() {
return copy(mConfig.tetherableUsbRegexs);
}
- public String[] getTetherableWifiRegexs() {
+ String[] getTetherableWifiRegexs() {
return copy(mConfig.tetherableWifiRegexs);
}
- public String[] getTetherableBluetoothRegexs() {
+ String[] getTetherableBluetoothRegexs() {
return copy(mConfig.tetherableBluetoothRegexs);
}
- public int setUsbTethering(boolean enable) {
+ int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
if (usbManager == null) {
@@ -1035,7 +1085,7 @@
}
// TODO review API - figure out how to delete these entirely.
- public String[] getTetheredIfaces() {
+ String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
@@ -1048,7 +1098,7 @@
return list.toArray(new String[list.size()]);
}
- public String[] getTetherableIfaces() {
+ String[] getTetherableIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
@@ -1061,13 +1111,13 @@
return list.toArray(new String[list.size()]);
}
- public String[] getTetheredDhcpRanges() {
+ String[] getTetheredDhcpRanges() {
// TODO: this is only valid for the old DHCP server. Latest search suggests it is only used
// by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers.
return mConfig.legacyDhcpRanges;
}
- public String[] getErroredIfaces() {
+ String[] getErroredIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
@@ -1518,8 +1568,8 @@
}
if (DBG) {
- Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() +
- " live requests:");
+ Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size()
+ + " live requests:");
for (IpServer o : mNotifyList) {
Log.d(TAG, " " + o);
}
@@ -1588,7 +1638,7 @@
transitionTo(mInitialState);
break;
default:
- retValue = false;
+ retValue = false;
}
return retValue;
}
@@ -1625,7 +1675,7 @@
notify(IpServer.CMD_START_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
}
@@ -1636,7 +1686,7 @@
notify(IpServer.CMD_STOP_TETHERING_ERROR);
try {
mNMService.setIpForwardingEnabled(false);
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
}
@@ -1647,10 +1697,10 @@
notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR);
try {
mNMService.stopTethering();
- } catch (Exception e) {}
+ } catch (Exception e) { }
try {
mNMService.setIpForwardingEnabled(false);
- } catch (Exception e) {}
+ } catch (Exception e) { }
}
}
@@ -1731,55 +1781,66 @@
}
}
- public void systemReady() {
+ private void startTrackDefaultNetwork() {
mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(),
mEntitlementMgr);
}
/** Get the latest value of the tethering entitlement check. */
- public void getLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
+ void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi) {
if (receiver != null) {
- mEntitlementMgr.getLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+ mEntitlementMgr.requestLatestTetheringEntitlementResult(type, receiver,
+ showEntitlementUi);
}
}
/** Register tethering event callback */
- public void registerTetheringEventCallback(ITetheringEventCallback callback) {
+ void registerTetherInternalCallback(ITetherInternalCallback callback) {
mHandler.post(() -> {
+ mTetherInternalCallback = callback;
try {
- callback.onUpstreamChanged(mTetherUpstream);
+ mTetherInternalCallback.onCallbackCreated(mTetherUpstream,
+ mConfig.toStableParcelable(), mTetherStatesParcel);
} catch (RemoteException e) {
// Not really very much to do here.
}
- mTetheringEventCallbacks.register(callback);
- });
- }
-
- /** Unregister tethering event callback */
- public void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
- mHandler.post(() -> {
- mTetheringEventCallbacks.unregister(callback);
});
}
private void reportUpstreamChanged(Network network) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
+ // Don't need to synchronized mTetherInternalCallback because all the usage of this variable
+ // should run at the same thread.
+ if (mTetherInternalCallback == null) return;
+
try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onUpstreamChanged(network);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
+ mTetherInternalCallback.onUpstreamChanged(network);
+ } catch (RemoteException e) {
+ // Not really very much to do here.
}
}
- @Override
- public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ private void reportConfigurationChanged(TetheringConfigurationParcel config) {
+ if (mTetherInternalCallback == null) return;
+
+ try {
+ mTetherInternalCallback.onConfigurationChanged(config);
+ } catch (RemoteException e) {
+ // Not really very much to do here.
+ }
+ }
+
+ private void reportTetherStateChanged(TetherStatesParcel states) {
+ if (mTetherInternalCallback == null) return;
+
+ try {
+ mTetherInternalCallback.onTetherStatesChanged(states);
+ } catch (RemoteException e) {
+ // Not really very much to do here.
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
// Binder.java closes the resource for us.
@SuppressWarnings("resource")
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1838,7 +1899,7 @@
pw.println("Log:");
pw.increaseIndent();
- if (argsContain(args, SHORT_ARG)) {
+ if (argsContain(args, "--short")) {
pw.println("<log removed for brevity>");
} else {
mLog.dump(fd, pw, args);
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index ca9b168..0ab4d63 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -38,6 +38,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.TetheringConfigurationParcel;
import android.net.util.SharedLog;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -384,4 +385,32 @@
}
return false;
}
+
+ /**
+ * Convert this TetheringConfiguration to a TetheringConfigurationParcel.
+ */
+ public TetheringConfigurationParcel toStableParcelable() {
+ final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
+ parcel.subId = subId;
+ parcel.tetherableUsbRegexs = tetherableUsbRegexs;
+ parcel.tetherableWifiRegexs = tetherableWifiRegexs;
+ parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
+ parcel.isDunRequired = isDunRequired;
+ parcel.chooseUpstreamAutomatically = chooseUpstreamAutomatically;
+
+ int[] preferredTypes = new int[preferredUpstreamIfaceTypes.size()];
+ int index = 0;
+ for (Integer type : preferredUpstreamIfaceTypes) {
+ preferredTypes[index++] = type;
+ }
+ parcel.preferredUpstreamIfaceTypes = preferredTypes;
+
+ parcel.legacyDhcpRanges = legacyDhcpRanges;
+ parcel.defaultIPv4DNS = defaultIPv4DNS;
+ parcel.enableLegacyDhcpServer = enableLegacyDhcpServer;
+ parcel.provisioningApp = provisioningApp;
+ parcel.provisioningAppNoUi = provisioningAppNoUi;
+ parcel.provisioningCheckPeriod = provisioningCheckPeriod;
+ return parcel;
+ }
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
new file mode 100644
index 0000000..0ba8412
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -0,0 +1,151 @@
+/*
+ * 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.connectivity.tethering;
+
+import android.content.Context;
+import android.net.INetd;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.net.NetworkRequest;
+import android.net.ip.IpServer;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Looper;
+import android.os.ServiceManager;
+
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+
+
+/**
+ * Capture tethering dependencies, for injection.
+ *
+ * @hide
+ */
+public class TetheringDependencies {
+ /**
+ * Get a reference to the offload hardware interface to be used by tethering.
+ */
+ public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+ return new OffloadHardwareInterface(h, log);
+ }
+
+ /**
+ * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+ */
+ public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
+ SharedLog log, int what) {
+ return new UpstreamNetworkMonitor(ctx, target, log, what);
+ }
+
+ /**
+ * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+ */
+ public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+ ArrayList<IpServer> notifyList, SharedLog log) {
+ return new IPv6TetheringCoordinator(notifyList, log);
+ }
+
+ /**
+ * Get dependencies to be used by IpServer.
+ */
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies();
+ }
+
+ /**
+ * Indicates whether tethering is supported on the device.
+ */
+ public boolean isTetheringSupported() {
+ return true;
+ }
+
+ /**
+ * Get the NetworkRequest that should be fulfilled by the default network.
+ */
+ public NetworkRequest getDefaultNetworkRequest() {
+ return null;
+ }
+
+ /**
+ * Get a reference to the EntitlementManager to be used by tethering.
+ */
+ public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, int what) {
+ return new EntitlementManager(ctx, target, log, what);
+ }
+
+ /**
+ * Generate a new TetheringConfiguration according to input sub Id.
+ */
+ public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
+ int subId) {
+ return new TetheringConfiguration(ctx, log, subId);
+ }
+
+ /**
+ * Get a reference to INetworkManagementService to registerTetheringStatsProvider from
+ * OffloadController. Note: This should be removed soon by Usage refactor work in R
+ * development cycle.
+ */
+ public INetworkManagementService getINetworkManagementService() {
+ return INetworkManagementService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));
+ }
+
+ /**
+ * Get a reference to INetworkStatsService to force update tethering usage.
+ * Note: This should be removed in R development cycle.
+ */
+ public INetworkStatsService getINetworkStatsService() {
+ return INetworkStatsService.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+ }
+
+ /**
+ * Get a reference to INetworkPolicyManager to be used by tethering.
+ */
+ public INetworkPolicyManager getINetworkPolicyManager() {
+ return INetworkPolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+ }
+
+ /**
+ * Get a reference to INetd to be used by tethering.
+ */
+ public INetd getINetd(Context context) {
+ return INetd.Stub.asInterface(
+ (IBinder) context.getSystemService(Context.NETD_SERVICE));
+ }
+
+ /**
+ * Get tethering thread looper.
+ */
+ public Looper getTetheringLooper() {
+ return null;
+ }
+
+ /**
+ * Get Context of TetheringSerice.
+ */
+ public Context getContext() {
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
similarity index 100%
rename from services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
rename to packages/Tethering/src/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
new file mode 100644
index 0000000..456f2f7
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -0,0 +1,178 @@
+/*
+ * 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.connectivity.tethering;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.ITetherInternalCallback;
+import android.net.ITetheringConnector;
+import android.net.NetworkRequest;
+import android.net.util.SharedLog;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
+import android.provider.Settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Android service used to manage tethering.
+ *
+ * <p>The service returns a binder for the system server to communicate with the tethering.
+ */
+public class TetheringService extends Service {
+ private static final String TAG = TetheringService.class.getSimpleName();
+
+ private final SharedLog mLog = new SharedLog(TAG);
+ private TetheringConnector mConnector;
+ private Context mContext;
+ private TetheringDependencies mDeps;
+ private Tethering mTethering;
+
+ @Override
+ public void onCreate() {
+ mLog.mark("onCreate");
+ mDeps = getTetheringDependencies();
+ mContext = mDeps.getContext();
+ mTethering = makeTethering(mDeps);
+ }
+
+ /**
+ * Make a reference to Tethering object.
+ */
+ @VisibleForTesting
+ public Tethering makeTethering(TetheringDependencies deps) {
+ return new Tethering(deps);
+ }
+
+ /**
+ * Create a binder connector for the system server to communicate with the tethering.
+ */
+ private synchronized IBinder makeConnector() {
+ if (mConnector == null) {
+ mConnector = new TetheringConnector(mTethering);
+ }
+ return mConnector;
+ }
+
+ @NonNull
+ @Override
+ public IBinder onBind(Intent intent) {
+ mLog.mark("onBind");
+ return makeConnector();
+ }
+
+ private static class TetheringConnector extends ITetheringConnector.Stub {
+ private final Tethering mService;
+
+ TetheringConnector(Tethering tether) {
+ mService = tether;
+ }
+
+ @Override
+ public void tether(String iface) {
+ mService.tether(iface);
+ }
+
+ @Override
+ public void untether(String iface) {
+ mService.untether(iface);
+ }
+
+ @Override
+ public void setUsbTethering(boolean enable) {
+ mService.setUsbTethering(enable);
+ }
+
+ @Override
+ public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
+ mService.startTethering(type, receiver, showProvisioningUi);
+ }
+
+ @Override
+ public void stopTethering(int type) {
+ mService.stopTethering(type);
+ }
+
+ @Override
+ public void requestLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ mService.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+ }
+
+ @Override
+ public void registerTetherInternalCallback(ITetherInternalCallback callback) {
+ mService.registerTetherInternalCallback(callback);
+ }
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
+ @Nullable String[] args) {
+ mTethering.dump(fd, writer, args);
+ }
+
+ /**
+ * An injection method for testing.
+ */
+ @VisibleForTesting
+ public TetheringDependencies getTetheringDependencies() {
+ if (mDeps == null) {
+ mDeps = new TetheringDependencies() {
+ @Override
+ public NetworkRequest getDefaultNetworkRequest() {
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ return cm.getDefaultRequest();
+ }
+
+ @Override
+ public Looper getTetheringLooper() {
+ final HandlerThread tetherThread = new HandlerThread("android.tethering");
+ tetherThread.start();
+ return tetherThread.getLooper();
+ }
+
+ @Override
+ public boolean isTetheringSupported() {
+ int defaultVal =
+ SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1;
+ boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
+ return tetherSupported;
+ }
+
+ @Override
+ public Context getContext() {
+ return TetheringService.this;
+ }
+ };
+ }
+
+ return mDeps;
+ }
+}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 363be18..5b018df 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -18,7 +18,6 @@
name: "TetheringTests",
certificate: "platform",
srcs: [
- ":servicescore-tethering-src",
"src/**/*.java",
],
test_suites: ["device-tests"],
@@ -41,17 +40,3 @@
"libstaticjvmtiagent",
],
}
-
-// This group would be removed when tethering migration is done.
-filegroup {
- name: "tethering-tests-src",
- srcs: [
- "src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
- "src/com/android/server/connectivity/tethering/OffloadControllerTest.java",
- "src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
- "src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java",
- "src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
- "src/android/net/ip/IpServerTest.java",
- "src/android/net/util/InterfaceSetTest.java",
- ],
-}
diff --git a/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java b/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
similarity index 96%
rename from tests/net/java/android/net/util/VersionedBroadcastListenerTest.java
rename to packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
index 0d27d5b..5a9b6e3 100644
--- a/tests/net/java/android/net/util/VersionedBroadcastListenerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
@@ -51,7 +51,9 @@
private VersionedBroadcastListener mListener;
private int mCallbackCount;
- private void doCallback() { mCallbackCount++; }
+ private void doCallback() {
+ mCallbackCount++;
+ }
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
@@ -96,7 +98,7 @@
mListener.startListening();
for (int i = 0; i < 5; i++) {
sendBroadcast();
- assertEquals(i+1, mCallbackCount);
+ assertEquals(i + 1, mCallbackCount);
}
mListener.stopListening();
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 5217e26..99cf9e9 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -24,6 +24,9 @@
import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,6 +47,7 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
@@ -57,7 +61,6 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.MockableSystemProperties;
import org.junit.After;
import org.junit.Before;
@@ -65,6 +68,8 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
@@ -80,7 +85,6 @@
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
- @Mock private MockableSystemProperties mSystemProperties;
@Mock private Resources mResources;
@Mock private SharedLog mLog;
@Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
@@ -95,6 +99,7 @@
private TestStateMachine mSM;
private WrappedEntitlementManager mEnMgr;
private TetheringConfiguration mConfig;
+ private MockitoSession mMockingSession;
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
@@ -118,8 +123,8 @@
public int silentProvisionCount = 0;
public WrappedEntitlementManager(Context ctx, StateMachine target,
- SharedLog log, int what, MockableSystemProperties systemProperties) {
- super(ctx, target, log, what, systemProperties);
+ SharedLog log, int what) {
+ super(ctx, target, log, what);
}
public void reset() {
@@ -144,6 +149,15 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .spyStatic(SystemProperties.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+ // Don't disable tethering provisioning unless requested.
+ doReturn(false).when(
+ () -> SystemProperties.getBoolean(
+ eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY), anyBoolean()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
@@ -161,8 +175,7 @@
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mMockContext = new MockContext(mContext);
mSM = new TestStateMachine();
- mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE,
- mSystemProperties);
+ mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, EVENT_EM_UPDATE);
mEnMgr.setOnUiEntitlementFailedListener(mEntitlementFailedListener);
mConfig = new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.setTetheringConfigurationFetcher(() -> {
@@ -176,6 +189,7 @@
mSM.quit();
mSM = null;
}
+ mMockingSession.finishMocking();
}
private void setupForRequiredProvisioning() {
@@ -184,9 +198,6 @@
.thenReturn(PROVISIONING_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_app_no_ui))
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
- // Don't disable tethering provisioning unless requested.
- when(mSystemProperties.getBoolean(eq(EntitlementManager.DISABLE_PROVISIONING_SYSPROP_KEY),
- anyBoolean())).thenReturn(false);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mCarrierConfigManager);
@@ -244,7 +255,7 @@
}
@Test
- public void testGetLastEntitlementCacheValue() throws Exception {
+ public void testRequestLastEntitlementCacheValue() throws Exception {
final CountDownLatch mCallbacklatch = new CountDownLatch(1);
// 1. Entitlement check is not required.
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
@@ -255,7 +266,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -270,7 +281,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -284,7 +295,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
@@ -298,7 +309,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -312,7 +323,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(1, mEnMgr.uiProvisionCount);
@@ -326,7 +337,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_WIFI, receiver, true);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
@@ -339,7 +350,7 @@
mCallbacklatch.countDown();
}
};
- mEnMgr.getLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
+ mEnMgr.requestLatestTetheringEntitlementResult(TETHERING_USB, receiver, false);
mLooper.dispatchAll();
callbackTimeoutHelper(mCallbacklatch);
assertEquals(0, mEnMgr.uiProvisionCount);
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
similarity index 83%
rename from tests/net/java/com/android/server/connectivity/TetheringTest.java
rename to packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 9e5717b..0273ed3 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.connectivity;
+package com.android.server.connectivity.tethering;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -39,7 +39,9 @@
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
@@ -70,7 +72,7 @@
import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
-import android.net.ITetheringEventCallback;
+import android.net.ITetherInternalCallback;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -82,6 +84,8 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.TetherStatesParcel;
+import android.net.TetheringConfigurationParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
@@ -98,6 +102,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -109,6 +114,7 @@
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -116,11 +122,6 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
-import com.android.server.connectivity.tethering.OffloadHardwareInterface;
-import com.android.server.connectivity.tethering.TetheringConfiguration;
-import com.android.server.connectivity.tethering.TetheringDependencies;
-import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import org.junit.After;
import org.junit.Before;
@@ -154,7 +155,6 @@
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
@Mock private INetworkPolicyManager mPolicyManager;
- @Mock private MockableSystemProperties mSystemProperties;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
@Mock private TelephonyManager mTelephonyManager;
@@ -166,6 +166,7 @@
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
@Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
+ @Mock private UserManager mUserManager;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -184,28 +185,37 @@
private Tethering mTethering;
private PhoneStateListener mPhoneStateListener;
- private class MockContext extends BroadcastInterceptingContext {
- MockContext(Context base) {
+ private class TestContext extends BroadcastInterceptingContext {
+ TestContext(Context base) {
super(base);
}
@Override
- public ApplicationInfo getApplicationInfo() { return mApplicationInfo; }
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
@Override
- public ContentResolver getContentResolver() { return mContentResolver; }
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
@Override
- public String getPackageName() { return "TetheringTest"; }
+ public String getPackageName() {
+ return "TetheringTest";
+ }
@Override
- public Resources getResources() { return mResources; }
+ public Resources getResources() {
+ return mResources;
+ }
@Override
public Object getSystemService(String name) {
if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
if (Context.USB_SERVICE.equals(name)) return mUsbManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
+ if (Context.USER_SERVICE.equals(name)) return mUserManager;
return super.getSystemService(name);
}
@@ -266,14 +276,14 @@
}
public class MockTetheringDependencies extends TetheringDependencies {
- StateMachine upstreamNetworkMonitorMasterSM;
- ArrayList<IpServer> ipv6CoordinatorNotifyList;
- int isTetheringSupportedCalls;
+ StateMachine mUpstreamNetworkMonitorMasterSM;
+ ArrayList<IpServer> mIpv6CoordinatorNotifyList;
+ int mIsTetheringSupportedCalls;
public void reset() {
- upstreamNetworkMonitorMasterSM = null;
- ipv6CoordinatorNotifyList = null;
- isTetheringSupportedCalls = 0;
+ mUpstreamNetworkMonitorMasterSM = null;
+ mIpv6CoordinatorNotifyList = null;
+ mIsTetheringSupportedCalls = 0;
}
@Override
@@ -284,14 +294,14 @@
@Override
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
StateMachine target, SharedLog log, int what) {
- upstreamNetworkMonitorMasterSM = target;
+ mUpstreamNetworkMonitorMasterSM = target;
return mUpstreamNetworkMonitor;
}
@Override
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<IpServer> notifyList, SharedLog log) {
- ipv6CoordinatorNotifyList = notifyList;
+ mIpv6CoordinatorNotifyList = notifyList;
return mIPv6TetheringCoordinator;
}
@@ -302,7 +312,7 @@
@Override
public boolean isTetheringSupported() {
- isTetheringSupportedCalls++;
+ mIsTetheringSupportedCalls++;
return true;
}
@@ -311,6 +321,36 @@
int subId) {
return new MockTetheringConfiguration(ctx, log, subId);
}
+
+ @Override
+ public INetworkManagementService getINetworkManagementService() {
+ return mNMService;
+ }
+
+ @Override
+ public INetworkStatsService getINetworkStatsService() {
+ return mStatsService;
+ }
+
+ @Override
+ public INetworkPolicyManager getINetworkPolicyManager() {
+ return mPolicyManager;
+ }
+
+ @Override
+ public INetd getINetd(Context context) {
+ return mNetd;
+ }
+
+ @Override
+ public Looper getTetheringLooper() {
+ return mLooper.getLooper();
+ }
+
+ @Override
+ public Context getContext() {
+ return mServiceContext;
+ }
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -345,7 +385,7 @@
final NetworkCapabilities capabilities = new NetworkCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
}
@@ -390,7 +430,7 @@
when(mRouterAdvertisementDaemon.start())
.thenReturn(true);
- mServiceContext = new MockContext(mContext);
+ mServiceContext = new TestContext(mContext);
mContentResolver = new MockContentResolver(mServiceContext);
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
@@ -403,9 +443,9 @@
};
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
- mTetheringDependencies.reset();
mTethering = makeTethering();
verify(mNMService).registerTetheringStatsProvider(any(), anyString());
+ verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
ArgumentCaptor.forClass(PhoneStateListener.class);
verify(mTelephonyManager).listen(phoneListenerCaptor.capture(),
@@ -414,9 +454,8 @@
}
private Tethering makeTethering() {
- return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
- mLooper.getLooper(), mSystemProperties,
- mTetheringDependencies);
+ mTetheringDependencies.reset();
+ return new Tethering(mTetheringDependencies);
}
@After
@@ -507,7 +546,7 @@
// it creates a IpServer and sends out a broadcast indicating that the
// interface is "available".
if (emulateInterfaceStatusChanged) {
- assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -584,7 +623,7 @@
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
// This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
// and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
// Emulate externally-visible WifiManager effects, when hotspot mode
// is being torn down.
@@ -617,8 +656,7 @@
argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
eq(IpServer.STATE_TETHERED));
- for (IpServer ipSrv :
- mTetheringDependencies.ipv6CoordinatorNotifyList) {
+ for (IpServer ipSrv : mTetheringDependencies.mIpv6CoordinatorNotifyList) {
NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0,
upstreamState.linkProperties.isIpv6Provisioned()
@@ -650,7 +688,7 @@
@Test
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
- mTethering = makeTethering();
+ sendConfigurationChanged();
final NetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
sendIPv6TetherUpdates(upstreamState);
@@ -719,7 +757,7 @@
.thenReturn(upstreamState);
// Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
- mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+ mTetheringDependencies.mUpstreamNetworkMonitorMasterSM.sendMessage(
Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
0,
@@ -784,7 +822,7 @@
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
mLooper.dispatchAll();
- assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(1, mTetheringDependencies.mIsTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
@@ -828,7 +866,7 @@
verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
// This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
// and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
/////
// We do not currently emulate any upstream being found.
@@ -901,7 +939,7 @@
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
// There are 3 state change event:
// AVAILABLE -> STATE_TETHERED -> STATE_AVAILABLE.
- assertEquals(3, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(3, mTetheringDependencies.mIsTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
// This is called, but will throw.
verify(mNMService, times(1)).setIpForwardingEnabled(true);
@@ -918,26 +956,26 @@
verifyNoMoreInteractions(mNMService);
}
- private void userRestrictionsListenerBehaviour(
- boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList,
- int expectedInteractionsWithShowNotification) throws Exception {
- final int userId = 0;
- final Bundle currRestrictions = new Bundle();
+ private void runUserRestrictionsChange(
+ boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList,
+ int expectedInteractionsWithShowNotification) throws Exception {
final Bundle newRestrictions = new Bundle();
- Tethering tethering = mock(Tethering.class);
- Tethering.TetheringUserRestrictionListener turl =
- new Tethering.TetheringUserRestrictionListener(tethering);
-
- currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow);
newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow);
- when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList);
+ final Tethering mockTethering = mock(Tethering.class);
+ when(mockTethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList);
+ when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
- turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions);
+ final Tethering.UserRestrictionActionListener ural =
+ new Tethering.UserRestrictionActionListener(mUserManager, mockTethering);
+ ural.mDisallowTethering = currentDisallow;
- verify(tethering, times(expectedInteractionsWithShowNotification))
+ ural.onUserRestrictionsChanged();
+
+ verify(mockTethering, times(expectedInteractionsWithShowNotification))
.showTetheredNotification(anyInt(), eq(false));
- verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll();
+ verify(mockTethering, times(expectedInteractionsWithShowNotification))
+ .untetherAll();
}
@Test
@@ -947,7 +985,7 @@
final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 0;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
expectedInteractionsWithShowNotification);
}
@@ -958,7 +996,7 @@
final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 1;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
expectedInteractionsWithShowNotification);
}
@@ -969,7 +1007,7 @@
final boolean nextDisallow = false;
final int expectedInteractionsWithShowNotification = 0;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
expectedInteractionsWithShowNotification);
}
@@ -980,7 +1018,7 @@
final boolean nextDisallow = false;
final int expectedInteractionsWithShowNotification = 0;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
expectedInteractionsWithShowNotification);
}
@@ -991,27 +1029,59 @@
boolean currDisallow = true;
boolean nextDisallow = true;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
expectedInteractionsWithShowNotification);
currDisallow = false;
nextDisallow = false;
- userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ runUserRestrictionsChange(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
expectedInteractionsWithShowNotification);
}
- private class TestTetheringEventCallback extends ITetheringEventCallback.Stub {
+ private class TestTetherInternalCallback extends ITetherInternalCallback.Stub {
private final ArrayList<Network> mActualUpstreams = new ArrayList<>();
+ private final ArrayList<TetheringConfigurationParcel> mTetheringConfigs =
+ new ArrayList<>();
+ private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>();
+ // This function will remove the recorded callbacks, so it must be called once for
+ // each callback. If this is called after multiple callback, the order matters.
+ // onCallbackCreated counts as the first call to expectUpstreamChanged with
+ // @see onCallbackCreated.
public void expectUpstreamChanged(Network... networks) {
+ if (networks == null) {
+ assertNoUpstreamChangeCallback();
+ return;
+ }
+
final ArrayList<Network> expectedUpstreams =
new ArrayList<Network>(Arrays.asList(networks));
for (Network upstream : expectedUpstreams) {
// throws OOB if no expectations
assertEquals(mActualUpstreams.remove(0), upstream);
}
- assertNoCallback();
+ assertNoUpstreamChangeCallback();
+ }
+
+ // This function will remove the recorded callbacks, so it must be called once
+ // for each callback. If this is called after multiple callback, the order matters.
+ // onCallbackCreated counts as the first call to onConfigurationChanged with
+ // @see onCallbackCreated.
+ public void expectConfigurationChanged(TetheringConfigurationParcel... tetherConfigs) {
+ final ArrayList<TetheringConfigurationParcel> expectedTetherConfig =
+ new ArrayList<TetheringConfigurationParcel>(Arrays.asList(tetherConfigs));
+ for (TetheringConfigurationParcel config : expectedTetherConfig) {
+ // throws OOB if no expectations
+ final TetheringConfigurationParcel actualConfig = mTetheringConfigs.remove(0);
+ assertTetherConfigParcelEqual(actualConfig, config);
+ }
+ assertNoConfigChangeCallback();
+ }
+
+ public TetherStatesParcel pollTetherStatesChanged() {
+ assertStateChangeCallback();
+ return mTetherStates.remove(0);
}
@Override
@@ -1019,48 +1089,93 @@
mActualUpstreams.add(network);
}
- public void assertNoCallback() {
+ @Override
+ public void onConfigurationChanged(TetheringConfigurationParcel config) {
+ mTetheringConfigs.add(config);
+ }
+
+ @Override
+ public void onTetherStatesChanged(TetherStatesParcel states) {
+ mTetherStates.add(states);
+ }
+
+ @Override
+ public void onCallbackCreated(Network network, TetheringConfigurationParcel config,
+ TetherStatesParcel states) {
+ mActualUpstreams.add(network);
+ mTetheringConfigs.add(config);
+ mTetherStates.add(states);
+ }
+
+ public void assertNoUpstreamChangeCallback() {
assertTrue(mActualUpstreams.isEmpty());
}
+
+ public void assertNoConfigChangeCallback() {
+ assertTrue(mTetheringConfigs.isEmpty());
+ }
+
+ public void assertStateChangeCallback() {
+ assertFalse(mTetherStates.isEmpty());
+ }
+
+ private void assertTetherConfigParcelEqual(@NonNull TetheringConfigurationParcel actual,
+ @NonNull TetheringConfigurationParcel expect) {
+ assertEquals(actual.subId, expect.subId);
+ assertArrayEquals(actual.tetherableUsbRegexs, expect.tetherableUsbRegexs);
+ assertArrayEquals(actual.tetherableWifiRegexs, expect.tetherableWifiRegexs);
+ assertArrayEquals(actual.tetherableBluetoothRegexs, expect.tetherableBluetoothRegexs);
+ assertEquals(actual.isDunRequired, expect.isDunRequired);
+ assertEquals(actual.chooseUpstreamAutomatically, expect.chooseUpstreamAutomatically);
+ assertArrayEquals(actual.preferredUpstreamIfaceTypes,
+ expect.preferredUpstreamIfaceTypes);
+ assertArrayEquals(actual.legacyDhcpRanges, expect.legacyDhcpRanges);
+ assertArrayEquals(actual.defaultIPv4DNS, expect.defaultIPv4DNS);
+ assertEquals(actual.enableLegacyDhcpServer, expect.enableLegacyDhcpServer);
+ assertArrayEquals(actual.provisioningApp, expect.provisioningApp);
+ assertEquals(actual.provisioningAppNoUi, expect.provisioningAppNoUi);
+ assertEquals(actual.provisioningCheckPeriod, expect.provisioningCheckPeriod);
+ }
}
@Test
- public void testRegisterTetheringEventCallback() throws Exception {
- TestTetheringEventCallback callback1 = new TestTetheringEventCallback();
- TestTetheringEventCallback callback2 = new TestTetheringEventCallback();
+ public void testRegisterTetherInternalCallback() throws Exception {
+ TestTetherInternalCallback callback = new TestTetherInternalCallback();
- // 1. Register one callback and run usb tethering.
- mTethering.registerTetheringEventCallback(callback1);
+ // 1. Register one callback before running any tethering.
+ mTethering.registerTetherInternalCallback(callback);
mLooper.dispatchAll();
- callback1.expectUpstreamChanged(new Network[] {null});
+ callback.expectUpstreamChanged(new Network[] {null});
+ callback.expectConfigurationChanged(
+ mTethering.getTetheringConfiguration().toStableParcelable());
+ TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
+ assertEquals(tetherState, null);
+ // 2. Enable wifi tethering
NetworkState upstreamState = buildMobileDualStackUpstreamState();
- runUsbTethering(upstreamState);
- callback1.expectUpstreamChanged(upstreamState.network);
- // 2. Register second callback.
- mTethering.registerTetheringEventCallback(callback2);
- mLooper.dispatchAll();
- callback2.expectUpstreamChanged(upstreamState.network);
- // 3. Disable usb tethering.
- mTethering.stopTethering(TETHERING_USB);
- mLooper.dispatchAll();
- sendUsbBroadcast(false, false, false);
- mLooper.dispatchAll();
- callback1.expectUpstreamChanged(new Network[] {null});
- callback2.expectUpstreamChanged(new Network[] {null});
- // 4. Unregister first callback and run hotspot.
- mTethering.unregisterTetheringEventCallback(callback1);
- mLooper.dispatchAll();
when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
.thenReturn(upstreamState);
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
- mTethering.startTethering(TETHERING_WIFI, null, false);
- mLooper.dispatchAll();
mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+ mLooper.dispatchAll();
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+
+ mTethering.startTethering(TETHERING_WIFI, null, false);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
mLooper.dispatchAll();
- callback1.assertNoCallback();
- callback2.expectUpstreamChanged(upstreamState.network);
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.tetheredList, new String[] {TEST_WLAN_IFNAME});
+ callback.expectUpstreamChanged(upstreamState.network);
+
+ // 3. Disable wifi tethering.
+ mTethering.stopTethering(TETHERING_WIFI);
+ sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
+ mLooper.dispatchAll();
+ tetherState = callback.pollTetherStatesChanged();
+ assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME});
+ mLooper.dispatchAll();
+ callback.expectUpstreamChanged(new Network[] {null});
}
@Test
@@ -1091,7 +1206,7 @@
verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
// This will be called twice, one is on entering IpServer.STATE_AVAILABLE,
// and another one is on IpServer.STATE_TETHERED/IpServer.STATE_LOCAL_ONLY.
- assertEquals(2, mTetheringDependencies.isTetheringSupportedCalls);
+ assertEquals(2, mTetheringDependencies.mIsTetheringSupportedCalls);
assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 1c2c640..8a4a1c6 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -153,6 +153,7 @@
Slog.v(TAG, "lockGen=" + lockGeneration + " : lockChanged=" + lockChanged);
Slog.v(TAG, "sysEligble=" + sysEligible);
Slog.v(TAG, "lockEligible=" + lockEligible);
+ Slog.v(TAG, "hasLockWallpaper=" + hasLockWallpaper);
}
// only back up the wallpapers if we've been told they're eligible
@@ -174,6 +175,17 @@
prefs.edit().putInt(SYSTEM_GENERATION, sysGeneration).apply();
}
+ // If there's no lock wallpaper, then we have nothing to add to the backup.
+ if (lockGeneration == -1) {
+ if (lockChanged && lockImageStage.exists()) {
+ if (DEBUG) Slog.v(TAG, "Removed lock wallpaper; deleting");
+ lockImageStage.delete();
+ }
+ if (DEBUG) Slog.v(TAG, "No lock paper set, add nothing to backup");
+ prefs.edit().putInt(LOCK_GENERATION, lockGeneration).apply();
+ return;
+ }
+
// Don't try to store the lock image if we overran our quota last time
if (lockEligible && hasLockWallpaper && mLockWallpaperFile.exists() && !mQuotaExceeded) {
if (lockChanged || !lockImageStage.exists()) {
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
index 46a7dfe..255fdef 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/tests/WallpaperBackupAgentTest.java
@@ -19,8 +19,13 @@
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -40,6 +45,7 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -53,78 +59,109 @@
private static final String SYSTEM_GENERATION = "system_gen";
private static final String LOCK_GENERATION = "lock_gen";
+ private static final int TEST_SYSTEM_WALLPAPER_ID = 1;
+ private static final int TEST_LOCK_WALLPAPER_ID = 2;
+
@Mock private FullBackupDataOutput mOutput;
@Mock private WallpaperManager mWallpaperManager;
@Mock private SharedPreferences mSharedPreferences;
+ @Mock private SharedPreferences.Editor mSharedPreferenceEditor;
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private ContextWithServiceOverrides mContext;
+ private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ when(mSharedPreferences.edit()).thenReturn(mSharedPreferenceEditor);
+ when(mSharedPreferenceEditor.putInt(anyString(), anyInt()))
+ .thenReturn(mSharedPreferenceEditor);
+ doNothing().when(mSharedPreferenceEditor).apply();
+
mContext = new ContextWithServiceOverrides(ApplicationProvider.getApplicationContext());
mContext.injectSystemService(WallpaperManager.class, mWallpaperManager);
mContext.setSharedPreferencesOverride(mSharedPreferences);
+
+ mWallpaperBackupAgent = new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
+ mWallpaperBackupAgent.attach(mContext);
+ mWallpaperBackupAgent.onCreate();
}
@Test
public void testOnFullBackup_withNoChanges_onlyBacksUpEmptyFile() throws IOException {
- WallpaperBackupAgent wallpaperBackupAgent = new WallpaperBackupAgent();
- initialiseAgent(wallpaperBackupAgent);
+ mockBackedUpState();
+ mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
- when(mWallpaperManager.getWallpaperIdForUser(eq(FLAG_SYSTEM), eq(UserHandle.USER_SYSTEM)))
- .thenReturn(1);
- when(mWallpaperManager.getWallpaperIdForUser(eq(FLAG_LOCK), eq(UserHandle.USER_SYSTEM)))
- .thenReturn(1);
- when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(1);
- when(mSharedPreferences.getInt(eq(LOCK_GENERATION), eq(-1))).thenReturn(1);
+ mWallpaperBackupAgent.onFullBackup(mOutput);
- wallpaperBackupAgent.onFullBackup(mOutput);
-
- verify(mOutput); // Backup of empty file only
+ assertThat(mWallpaperBackupAgent.mBackedUpFiles.size()).isEqualTo(1);
+ assertThat(mWallpaperBackupAgent.mBackedUpFiles.get(0).getName()).isEqualTo("empty");
}
@Test
public void testOnFullBackup_withOnlyChangedSystem_updatesTheSharedPreferences()
throws IOException {
- // Create a system wallpaper file
- mTemporaryFolder.newFile("wallpaper_orig");
- // Create stageing file to simulate he wallpaper being ready to back up
- new File(mContext.getFilesDir(), "wallpaper-stage").createNewFile();
+ mockSystemWallpaperReadyToBackUp();
+ mockUnbackedUpState();
+ mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
- WallpaperBackupAgent wallpaperBackupAgent =
- new IsolatedWallpaperBackupAgent(mTemporaryFolder.getRoot());
- initialiseAgent(wallpaperBackupAgent);
+ mWallpaperBackupAgent.onFullBackup(mOutput);
- SharedPreferences.Editor preferenceEditor = mock(SharedPreferences.Editor.class);
+ verify(mSharedPreferenceEditor).putInt(eq(SYSTEM_GENERATION), eq(TEST_SYSTEM_WALLPAPER_ID));
+ }
+ @Test
+ public void testOnFullBackup_withLockChangedToMatchSystem_updatesTheSharedPreferences()
+ throws IOException {
+ mockBackedUpState();
+ mockSystemWallpaperReadyToBackUp();
+ mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, -1);
+
+ mWallpaperBackupAgent.onFullBackup(mOutput);
+
+ InOrder inOrder = inOrder(mSharedPreferenceEditor);
+ inOrder.verify(mSharedPreferenceEditor)
+ .putInt(eq(SYSTEM_GENERATION), eq(TEST_SYSTEM_WALLPAPER_ID));
+ inOrder.verify(mSharedPreferenceEditor).apply();
+ inOrder.verify(mSharedPreferenceEditor).putInt(eq(LOCK_GENERATION), eq(-1));
+ inOrder.verify(mSharedPreferenceEditor).apply();
+ }
+
+ private void mockUnbackedUpState() {
+ mockCurrentWallpapers(TEST_SYSTEM_WALLPAPER_ID, TEST_LOCK_WALLPAPER_ID);
+ when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(-1);
+ when(mSharedPreferences.getInt(eq(LOCK_GENERATION), eq(-1))).thenReturn(-1);
+ }
+
+ private void mockBackedUpState() {
+ when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1)))
+ .thenReturn(TEST_SYSTEM_WALLPAPER_ID);
+ when(mSharedPreferences.getInt(eq(LOCK_GENERATION), eq(-1)))
+ .thenReturn(TEST_LOCK_WALLPAPER_ID);
+ }
+
+ private void mockCurrentWallpapers(int systemWallpaperId, int lockWallpaperId) {
when(mWallpaperManager.getWallpaperIdForUser(eq(FLAG_SYSTEM), eq(UserHandle.USER_SYSTEM)))
- .thenReturn(2);
+ .thenReturn(systemWallpaperId);
when(mWallpaperManager.getWallpaperIdForUser(eq(FLAG_LOCK), eq(UserHandle.USER_SYSTEM)))
- .thenReturn(1);
+ .thenReturn(lockWallpaperId);
when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_SYSTEM))).thenReturn(true);
when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_LOCK))).thenReturn(true);
- when(mSharedPreferences.getInt(eq(SYSTEM_GENERATION), eq(-1))).thenReturn(1);
- when(mSharedPreferences.getInt(eq(LOCK_GENERATION), eq(-1))).thenReturn(1);
- when(mSharedPreferences.edit()).thenReturn(preferenceEditor);
- when(preferenceEditor.putInt(eq(SYSTEM_GENERATION), eq(2))).thenReturn(preferenceEditor);
-
- wallpaperBackupAgent.onFullBackup(mOutput);
-
- verify(preferenceEditor).putInt(eq(SYSTEM_GENERATION), eq(2));
}
- private void initialiseAgent(WallpaperBackupAgent agent) {
- agent.attach(mContext);
- agent.onCreate();
+ private void mockSystemWallpaperReadyToBackUp() throws IOException {
+ // Create a system wallpaper file
+ mTemporaryFolder.newFile("wallpaper_orig");
+ // Create staging file to simulate he wallpaper being ready to back up
+ new File(mContext.getFilesDir(), "wallpaper-stage").createNewFile();
}
- private static class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
+ private class IsolatedWallpaperBackupAgent extends WallpaperBackupAgent {
File mWallpaperBaseDirectory;
- List<File> mBackedUpFiles = new ArrayList();
+ List<File> mBackedUpFiles = new ArrayList<>();
IsolatedWallpaperBackupAgent(File wallpaperBaseDirectory) {
mWallpaperBaseDirectory = wallpaperBaseDirectory;
@@ -139,5 +176,10 @@
protected void backupFile(File file, FullBackupDataOutput data) {
mBackedUpFiles.add(file);
}
+
+ @Override
+ public SharedPreferences getSharedPreferences(File file, int mode) {
+ return mSharedPreferences;
+ }
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 222a6f2..e8c5299 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -67,6 +67,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.List;
import java.util.Set;
/**
@@ -1510,6 +1511,26 @@
}
/**
+ * Excludes keys from KV restore for a given package. The corresponding data will be excluded
+ * from the data set available the backup agent during restore. However, final list of keys
+ * that have been excluded will be passed to the agent to make it aware of the exclusions.
+ */
+ public void excludeKeysFromRestore(String packageName, List<String> keys) {
+ int userId = Binder.getCallingUserHandle().getIdentifier();
+ if (!isUserReadyForBackup(userId)) {
+ Slog.w(TAG, "Returning from excludeKeysFromRestore as backup for user" + userId +
+ " is not initialized yet");
+ return;
+ }
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "excludeKeysFromRestore()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.excludeKeysFromRestore(packageName, keys);
+ }
+ }
+
+ /**
* Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
* If the user is not registered with the service (either the user is locked or not eligible for
* the backup service) then return {@code null}.
diff --git a/services/backup/java/com/android/server/backup/DataChangedJournal.java b/services/backup/java/com/android/server/backup/DataChangedJournal.java
index 498185c..e75eb73 100644
--- a/services/backup/java/com/android/server/backup/DataChangedJournal.java
+++ b/services/backup/java/com/android/server/backup/DataChangedJournal.java
@@ -17,6 +17,7 @@
package com.android.server.backup;
import android.annotation.Nullable;
+import android.util.Slog;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
@@ -36,6 +37,7 @@
* reboot.
*/
public class DataChangedJournal {
+ private static final String TAG = "DataChangedJournal";
private static final String FILE_NAME_PREFIX = "journal";
/**
@@ -139,7 +141,12 @@
*/
static ArrayList<DataChangedJournal> listJournals(File journalDirectory) {
ArrayList<DataChangedJournal> journals = new ArrayList<>();
- for (File file : journalDirectory.listFiles()) {
+ File[] journalFiles = journalDirectory.listFiles();
+ if (journalFiles == null) {
+ Slog.w(TAG, "Failed to read journal files");
+ return journals;
+ }
+ for (File file : journalFiles) {
journals.add(new DataChangedJournal(file));
}
return journals;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 77888db..56b345b 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -158,6 +158,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.Set;
@@ -332,6 +333,8 @@
// locking around the pending-backup management
private final Object mQueueLock = new Object();
+ private final UserBackupPreferences mBackupPreferences;
+
// The thread performing the sequence of queued backups binds to each app's agent
// in succession. Bind notifications are asynchronously delivered through the
// Activity Manager; use this lock object to signal when a requested binding has
@@ -632,6 +635,8 @@
// the pending backup set
mBackupHandler.postDelayed(this::parseLeftoverJournals, INITIALIZATION_DELAY_MILLIS);
+ mBackupPreferences = new UserBackupPreferences(mContext, mBaseStateDir);
+
// Power management
mWakelock = new BackupWakeLock(
mPowerManager.newWakeLock(
@@ -1097,6 +1102,14 @@
}
}
+ public Map<String, Set<String>> getExcludedRestoreKeys(String... packages) {
+ return mBackupPreferences.getExcludedRestoreKeysForPackages(packages);
+ }
+
+ public Map<String, Set<String>> getAllExcludedRestoreKeys() {
+ return mBackupPreferences.getAllExcludedRestoreKeys();
+ }
+
/** Used for generating random salts or passwords. */
public byte[] randomBytes(int bits) {
byte[] array = new byte[bits / 8];
@@ -2746,6 +2759,14 @@
}
}
+ /**
+ * Excludes keys from KV restore for a given package. The keys won't be part of the data passed
+ * to the backup agent during restore.
+ */
+ public void excludeKeysFromRestore(String packageName, List<String> keys) {
+ mBackupPreferences.addExcludedKeys(packageName, keys);
+ }
+
private boolean startConfirmationUi(int token, String action) {
try {
Intent confIntent = new Intent(action);
@@ -3341,7 +3362,8 @@
restoreSet,
packageName,
token,
- listener);
+ listener,
+ getExcludedRestoreKeys(packageName));
mBackupHandler.sendMessage(msg);
} catch (Exception e) {
// Calling into the transport broke; back off and proceed with the installation.
diff --git a/services/backup/java/com/android/server/backup/UserBackupPreferences.java b/services/backup/java/com/android/server/backup/UserBackupPreferences.java
new file mode 100644
index 0000000..41b9719
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/UserBackupPreferences.java
@@ -0,0 +1,63 @@
+/*
+ * 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.backup;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/** Manages the persisted backup preferences per user. */
+public class UserBackupPreferences {
+ private static final String PREFERENCES_FILE = "backup_preferences";
+
+ private final SharedPreferences mPreferences;
+ private final SharedPreferences.Editor mEditor;
+
+ UserBackupPreferences(Context conext, File storageDir) {
+ File excludedKeysFile = new File(storageDir, PREFERENCES_FILE);
+ mPreferences = conext.getSharedPreferences(excludedKeysFile, Context.MODE_PRIVATE);
+ mEditor = mPreferences.edit();
+ }
+
+ void addExcludedKeys(String packageName, List<String> keys) {
+ Set<String> existingKeys =
+ new HashSet<>(mPreferences.getStringSet(packageName, Collections.emptySet()));
+ existingKeys.addAll(keys);
+ mEditor.putStringSet(packageName, existingKeys);
+ mEditor.commit();
+ }
+
+ Map<String, Set<String>> getExcludedRestoreKeysForPackages(String... packages) {
+ Map<String, Set<String>> excludedKeys = new HashMap<>();
+ for (String packageName : packages) {
+ excludedKeys.put(packageName,
+ mPreferences.getStringSet(packageName, Collections.emptySet()));
+ }
+ return excludedKeys;
+ }
+
+ Map<String, Set<String>> getAllExcludedRestoreKeys() {
+ return (Map<String, Set<String>>) mPreferences.getAll();
+ }
+}
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 059b1b9..8c48b84 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -279,7 +279,8 @@
params.pmToken,
params.isSystemRestore,
params.filterSet,
- params.listener);
+ params.listener,
+ params.excludedKeys);
synchronized (backupManagerService.getPendingRestores()) {
if (backupManagerService.isRestoreInProgress()) {
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index c9a6b60..09b7e35 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -24,6 +24,9 @@
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.transport.TransportClient;
+import java.util.Map;
+import java.util.Set;
+
public class RestoreParams {
public final TransportClient transportClient;
public final IRestoreObserver observer;
@@ -34,6 +37,7 @@
public final boolean isSystemRestore;
@Nullable public final String[] filterSet;
public final OnTaskFinishedListener listener;
+ public final Map<String, Set<String>> excludedKeys;
/**
* No kill after restore.
@@ -44,7 +48,8 @@
IBackupManagerMonitor monitor,
long token,
PackageInfo packageInfo,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
return new RestoreParams(
transportClient,
observer,
@@ -54,7 +59,8 @@
/* pmToken */ 0,
/* isSystemRestore */ false,
/* filterSet */ null,
- listener);
+ listener,
+ excludedKeys);
}
/**
@@ -67,7 +73,8 @@
long token,
String packageName,
int pmToken,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
String[] filterSet = {packageName};
return new RestoreParams(
transportClient,
@@ -78,7 +85,8 @@
pmToken,
/* isSystemRestore */ false,
filterSet,
- listener);
+ listener,
+ excludedKeys);
}
/**
@@ -89,7 +97,8 @@
IRestoreObserver observer,
IBackupManagerMonitor monitor,
long token,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
return new RestoreParams(
transportClient,
observer,
@@ -99,7 +108,8 @@
/* pmToken */ 0,
/* isSystemRestore */ true,
/* filterSet */ null,
- listener);
+ listener,
+ excludedKeys);
}
/**
@@ -112,7 +122,8 @@
long token,
String[] filterSet,
boolean isSystemRestore,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
return new RestoreParams(
transportClient,
observer,
@@ -122,7 +133,8 @@
/* pmToken */ 0,
isSystemRestore,
filterSet,
- listener);
+ listener,
+ excludedKeys);
}
private RestoreParams(
@@ -134,7 +146,8 @@
int pmToken,
boolean isSystemRestore,
@Nullable String[] filterSet,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
this.transportClient = transportClient;
this.observer = observer;
this.monitor = monitor;
@@ -144,5 +157,6 @@
this.isSystemRestore = isSystemRestore;
this.filterSet = filterSet;
this.listener = listener;
+ this.excludedKeys = excludedKeys;
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index 5a57cdc..c0f76c3 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -178,7 +178,8 @@
observer,
monitor,
token,
- listener),
+ listener,
+ mBackupManagerService.getAllExcludedRestoreKeys()),
"RestoreSession.restoreAll()");
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -271,7 +272,8 @@
token,
packages,
/* isSystemRestore */ packages.length > 1,
- listener),
+ listener,
+ mBackupManagerService.getExcludedRestoreKeys(packages)),
"RestoreSession.restorePackages(" + packages.length + " packages)");
} finally {
Binder.restoreCallingIdentity(oldId);
@@ -363,7 +365,8 @@
monitor,
token,
app,
- listener),
+ listener,
+ mBackupManagerService.getExcludedRestoreKeys(app.packageName)),
"RestoreSession.restorePackage(" + packageName + ")");
} finally {
Binder.restoreCallingIdentity(oldId);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 675a6eb..be597d7 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -52,6 +52,7 @@
import android.util.EventLog;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
@@ -77,6 +78,8 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@@ -151,6 +154,8 @@
// When finished call listener
private final OnTaskFinishedListener mListener;
+ private final Map<String, Set<String>> mExcludedKeys;
+
// Key/value: bookkeeping about staged data and files for agent access
private File mBackupDataName;
private File mStageName;
@@ -161,6 +166,17 @@
private final int mEphemeralOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+ @VisibleForTesting
+ PerformUnifiedRestoreTask(Map<String, Set<String>> excludedKeys) {
+ mExcludedKeys = excludedKeys;
+ mListener = null;
+ mAgentTimeoutParameters = null;
+ mTransportClient = null;
+ mTransportManager = null;
+ mEphemeralOpToken = 0;
+ mUserId = 0;
+ }
+
// This task can assume that the wakelock is properly held for it and doesn't have to worry
// about releasing it.
public PerformUnifiedRestoreTask(
@@ -173,7 +189,8 @@
int pmToken,
boolean isFullSystemRestore,
@Nullable String[] filterSet,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
this.backupManagerService = backupManagerService;
mUserId = backupManagerService.getUserId();
mTransportManager = backupManagerService.getTransportManager();
@@ -195,6 +212,8 @@
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
+ mExcludedKeys = excludedKeys;
+
if (targetPackage != null) {
// Single package restore
mAcceptSet = new ArrayList<>();
@@ -724,27 +743,7 @@
BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
- byte[] buffer = new byte[8192]; // will grow when needed
- while (in.readNextHeader()) {
- final String key = in.getKey();
- final int size = in.getDataSize();
-
- // is this a special key?
- if (key.equals(KEY_WIDGET_STATE)) {
- if (DEBUG) {
- Slog.i(TAG, "Restoring widget state for " + packageName);
- }
- mWidgetData = new byte[size];
- in.readEntityData(mWidgetData, 0, size);
- } else {
- if (size > buffer.length) {
- buffer = new byte[size];
- }
- in.readEntityData(buffer, 0, size);
- out.writeEntityHeader(key, size);
- out.writeEntityData(buffer, size);
- }
- }
+ filterExcludedKeys(packageName, in, out);
mBackupData.close();
}
@@ -783,6 +782,39 @@
}
}
+ @VisibleForTesting
+ void filterExcludedKeys(String packageName, BackupDataInput in, BackupDataOutput out)
+ throws Exception {
+ Set<String> excludedKeysForPackage = mExcludedKeys.get(packageName);
+
+ byte[] buffer = new byte[8192]; // will grow when needed
+ while (in.readNextHeader()) {
+ final String key = in.getKey();
+ final int size = in.getDataSize();
+
+ if (excludedKeysForPackage != null && excludedKeysForPackage.contains(key)) {
+ in.skipEntityData();
+ continue;
+ }
+
+ // is this a special key?
+ if (key.equals(KEY_WIDGET_STATE)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Restoring widget state for " + packageName);
+ }
+ mWidgetData = new byte[size];
+ in.readEntityData(mWidgetData, 0, size);
+ } else {
+ if (size > buffer.length) {
+ buffer = new byte[size];
+ }
+ in.readEntityData(buffer, 0, size);
+ out.writeEntityHeader(key, size);
+ out.writeEntityData(buffer, size);
+ }
+ }
+ }
+
// state RESTORE_FULL : restore one package via streaming engine
private void restoreFull() {
// None of this can run on the work looper here, so we spin asynchronous
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9291adc..203bc61 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -83,7 +83,6 @@
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
- ":tethering-servicescore-srcs",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
@@ -115,7 +114,6 @@
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.1-java",
"android.hardware.oemlock-V1.0-java",
- "android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
"android.hidl.manager-V1.2-java",
@@ -165,10 +163,8 @@
src: ":services.core.json.gz",
}
-// TODO: this should be removed after tethering migration done.
-filegroup {
- name: "servicescore-tethering-src",
- srcs: [
- "java/com/android/server/connectivity/MockableSystemProperties.java",
- ],
+platform_compat_config {
+ name: "services-core-platform-compat-config",
+ src: ":services.core.unboosted",
}
+
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 413a79d..de6cca5 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -768,9 +768,10 @@
* @param userId user to uninstall apex package for. Must be
* {@link android.os.UserHandle#USER_ALL}, otherwise failure will be reported.
* @param intentSender a {@link IntentSender} to send result of an uninstall to.
+ * @param flags flags about the uninstall.
*/
public abstract void uninstallApex(String packageName, long versionCode, int userId,
- IntentSender intentSender);
+ IntentSender intentSender, int flags);
/**
* Get fingerprint of build that updated the runtime permissions for a user.
diff --git a/services/core/java/android/os/UserManagerInternal.java b/services/core/java/android/os/UserManagerInternal.java
index 9a7cb3f..a2e9341 100644
--- a/services/core/java/android/os/UserManagerInternal.java
+++ b/services/core/java/android/os/UserManagerInternal.java
@@ -15,6 +15,7 @@
*/
package android.os;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -22,13 +23,24 @@
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* @hide Only for use within the system server.
*/
public abstract class UserManagerInternal {
- public static final int CAMERA_NOT_DISABLED = 0;
- public static final int CAMERA_DISABLED_LOCALLY = 1;
- public static final int CAMERA_DISABLED_GLOBALLY = 2;
+
+ public static final int OWNER_TYPE_DEVICE_OWNER = 0;
+ public static final int OWNER_TYPE_PROFILE_OWNER = 1;
+ public static final int OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE = 2;
+ public static final int OWNER_TYPE_NO_OWNER = 3;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {OWNER_TYPE_DEVICE_OWNER, OWNER_TYPE_PROFILE_OWNER,
+ OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, OWNER_TYPE_NO_OWNER})
+ public @interface OwnerType {
+ }
public interface UserRestrictionsListener {
/**
@@ -47,13 +59,19 @@
*
* @param userId target user id for the local restrictions.
* @param restrictions a bundle of user restrictions.
- * @param isDeviceOwner whether {@code userId} corresponds to device owner user id.
- * @param cameraRestrictionScope is camera disabled and if so what is the scope of restriction.
- * Should be one of {@link #CAMERA_NOT_DISABLED}, {@link #CAMERA_DISABLED_LOCALLY} or
- * {@link #CAMERA_DISABLED_GLOBALLY}
+ * @param restrictionOwnerType determines which admin {@code userId} corresponds to.
+ * The admin can be either
+ * {@link UserManagerInternal#OWNER_TYPE_DEVICE_OWNER},
+ * {@link UserManagerInternal#OWNER_TYPE_PROFILE_OWNER},
+ * {@link UserManagerInternal#OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE}
+ * or {@link UserManagerInternal#OWNER_TYPE_NO_OWNER}.
+ * If the admin is a DEVICE_OWNER or a PROFILE_OWNER_ORG_OWNED_DEVICE then
+ * a restriction may be applied globally depending on which restriction it is,
+ * otherwise it will be applied just on the current user.
+ * @see OwnerType
*/
public abstract void setDevicePolicyUserRestrictions(int userId, @Nullable Bundle restrictions,
- boolean isDeviceOwner, int cameraRestrictionScope);
+ @OwnerType int restrictionOwnerType);
/**
* Returns the "base" user restrictions.
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 798a4c6..a318844 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -74,6 +74,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.HashMap;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
@@ -695,6 +696,35 @@
return mIsHearingAidProfileSupported;
}
+ @Override
+ /** @hide */
+ public java.util.List<String> getSystemConfigEnabledProfilesForPackage(String packageName) {
+ if (Binder.getCallingUid() != Process.BLUETOOTH_UID) {
+ Slog.w(TAG, "getSystemConfigEnabledProfilesForPackage(): not allowed for non-bluetooth");
+ return null;
+ }
+
+ SystemConfig systemConfig = SystemConfig.getInstance();
+ if (systemConfig == null) {
+ return null;
+ }
+
+ android.util.ArrayMap<String, Boolean> componentEnabledStates =
+ systemConfig.getComponentsEnabledStates(packageName);
+ if (componentEnabledStates == null) {
+ return null;
+ }
+
+ ArrayList enabledProfiles = new ArrayList<String>();
+ for (Map.Entry<String, Boolean> entry : componentEnabledStates.entrySet()) {
+ if (entry.getValue()) {
+ enabledProfiles.add(entry.getKey());
+ }
+ }
+
+ return enabledProfiles;
+ }
+
// Monitor change of BLE scan only mode settings.
private void registerForBleScanModeChange() {
ContentObserver contentObserver = new ContentObserver(null) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bb7406a..a3a6172 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -108,6 +108,7 @@
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.SocketKeepalive;
+import android.net.TetheringManager;
import android.net.UidRange;
import android.net.Uri;
import android.net.VpnService;
@@ -187,9 +188,7 @@
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
-import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
-import com.android.server.connectivity.tethering.TetheringDependencies;
import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
@@ -233,7 +232,6 @@
private static final String DIAG_ARG = "--diag";
public static final String SHORT_ARG = "--short";
- private static final String TETHERING_ARG = "tethering";
private static final String NETWORK_ARG = "networks";
private static final String REQUEST_ARG = "requests";
@@ -280,7 +278,7 @@
private MockableSystemProperties mSystemProperties;
- private Tethering mTethering;
+ private TetheringManager mTetheringManager;
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
@@ -869,15 +867,10 @@
}
/**
- * @see Tethering
+ * Get a reference to the TetheringManager.
*/
- public Tethering makeTethering(@NonNull Context context,
- @NonNull INetworkManagementService nms,
- @NonNull INetworkStatsService statsService,
- @NonNull INetworkPolicyManager policyManager,
- @NonNull TetheringDependencies tetheringDeps) {
- return new Tethering(context, nms, statsService, policyManager,
- IoThread.get().getLooper(), getSystemProperties(), tetheringDeps);
+ public TetheringManager getTetheringManager() {
+ return TetheringManager.getInstance();
}
/**
@@ -932,6 +925,10 @@
return IIpConnectivityMetrics.Stub.asInterface(
ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
}
+
+ public IBatteryStats getBatteryStatsService() {
+ return BatteryStatsService.getService();
+ }
}
public ConnectivityService(Context context, INetworkManagementService netManager,
@@ -1075,8 +1072,7 @@
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mTethering = deps.makeTethering(mContext, mNMS, mStatsService, mPolicyManager,
- makeTetheringDependencies());
+ mTetheringManager = mDeps.getTetheringManager();
mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
@@ -1111,7 +1107,6 @@
mHandler);
try {
- mNMS.registerObserver(mTethering);
mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
@@ -1145,19 +1140,6 @@
registerPrivateDnsSettingsCallbacks();
}
- private TetheringDependencies makeTetheringDependencies() {
- return new TetheringDependencies() {
- @Override
- public boolean isTetheringSupported() {
- return ConnectivityService.this.isTetheringSupported();
- }
- @Override
- public NetworkRequest getDefaultNetworkRequest() {
- return mDefaultRequest;
- }
- };
- }
-
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
@@ -1909,7 +1891,9 @@
// TODO: relocate this specific callback in Tethering.
if (restrictBackground) {
log("onRestrictBackgroundChanged(true): disabling tethering");
- mTethering.untetherAll();
+ mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ mTetheringManager.stopTethering(ConnectivityManager.TETHERING_USB);
+ mTetheringManager.stopTethering(ConnectivityManager.TETHERING_BLUETOOTH);
}
}
};
@@ -2164,7 +2148,7 @@
opts.setMaxManifestReceiverApiLevel(Build.VERSION_CODES.M);
options = opts.toBundle();
}
- final IBatteryStats bs = BatteryStatsService.getService();
+ final IBatteryStats bs = mDeps.getBatteryStatsService();
try {
bs.noteConnectivityChanged(intent.getIntExtra(
ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
@@ -2193,7 +2177,6 @@
mPermissionMonitor.startMonitoring();
mProxyTracker.loadGlobalProxy();
registerNetdEventCallback();
- mTethering.systemReady();
synchronized (this) {
mSystemReady = true;
@@ -2405,9 +2388,6 @@
if (ArrayUtils.contains(args, DIAG_ARG)) {
dumpNetworkDiagnostics(pw);
return;
- } else if (ArrayUtils.contains(args, TETHERING_ARG)) {
- mTethering.dump(fd, pw, args);
- return;
} else if (ArrayUtils.contains(args, NETWORK_ARG)) {
dumpNetworks(pw);
return;
@@ -2469,10 +2449,13 @@
mLegacyTypeTracker.dump(pw);
pw.println();
- mTethering.dump(fd, pw, args);
+ mKeepaliveTracker.dump(pw);
pw.println();
- mKeepaliveTracker.dump(pw);
+ pw.println("TetheringManager logs:");
+ pw.increaseIndent();
+ TetheringManager.getInstance().dump(pw);
+ pw.decreaseIndent();
pw.println();
dumpAvoidBadWifiSettings(pw);
@@ -4004,7 +3987,7 @@
public int tether(String iface, String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
if (isTetheringSupported()) {
- return mTethering.tether(iface);
+ return mTetheringManager.tether(iface);
} else {
return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
}
@@ -4016,7 +3999,7 @@
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
if (isTetheringSupported()) {
- return mTethering.untether(iface);
+ return mTetheringManager.untether(iface);
} else {
return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
}
@@ -4028,7 +4011,7 @@
enforceTetherAccessPermission();
if (isTetheringSupported()) {
- return mTethering.getLastTetherError(iface);
+ return mTetheringManager.getLastTetherError(iface);
} else {
return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
}
@@ -4039,7 +4022,7 @@
public String[] getTetherableUsbRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
- return mTethering.getTetherableUsbRegexs();
+ return mTetheringManager.getTetherableUsbRegexs();
} else {
return new String[0];
}
@@ -4049,7 +4032,7 @@
public String[] getTetherableWifiRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
- return mTethering.getTetherableWifiRegexs();
+ return mTetheringManager.getTetherableWifiRegexs();
} else {
return new String[0];
}
@@ -4059,7 +4042,7 @@
public String[] getTetherableBluetoothRegexs() {
enforceTetherAccessPermission();
if (isTetheringSupported()) {
- return mTethering.getTetherableBluetoothRegexs();
+ return mTetheringManager.getTetherableBluetoothRegexs();
} else {
return new String[0];
}
@@ -4069,7 +4052,7 @@
public int setUsbTethering(boolean enable, String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
if (isTetheringSupported()) {
- return mTethering.setUsbTethering(enable);
+ return mTetheringManager.setUsbTethering(enable);
} else {
return ConnectivityManager.TETHER_ERROR_UNSUPPORTED;
}
@@ -4080,25 +4063,25 @@
@Override
public String[] getTetherableIfaces() {
enforceTetherAccessPermission();
- return mTethering.getTetherableIfaces();
+ return mTetheringManager.getTetherableIfaces();
}
@Override
public String[] getTetheredIfaces() {
enforceTetherAccessPermission();
- return mTethering.getTetheredIfaces();
+ return mTetheringManager.getTetheredIfaces();
}
@Override
public String[] getTetheringErroredIfaces() {
enforceTetherAccessPermission();
- return mTethering.getErroredIfaces();
+ return mTetheringManager.getTetheringErroredIfaces();
}
@Override
public String[] getTetheredDhcpRanges() {
enforceConnectivityInternalPermission();
- return mTethering.getTetheredDhcpRanges();
+ return mTetheringManager.getTetheredDhcpRanges();
}
@Override
@@ -4126,7 +4109,8 @@
Binder.restoreCallingIdentity(token);
}
- return tetherEnabledInSettings && adminUser && mTethering.hasTetherableConfiguration();
+ return tetherEnabledInSettings && adminUser
+ && mTetheringManager.hasTetherableConfiguration();
}
@Override
@@ -4137,13 +4121,13 @@
receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
return;
}
- mTethering.startTethering(type, receiver, showProvisioningUi);
+ mTetheringManager.startTethering(type, receiver, showProvisioningUi);
}
@Override
public void stopTethering(int type, String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.stopTethering(type);
+ mTetheringManager.stopTethering(type);
}
/**
@@ -4157,7 +4141,8 @@
public void getLatestTetheringEntitlementResult(int type, ResultReceiver receiver,
boolean showEntitlementUi, String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.getLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
+ mTetheringManager.requestLatestTetheringEntitlementResult(
+ type, receiver, showEntitlementUi);
}
/** Register tethering event callback. */
@@ -4165,7 +4150,7 @@
public void registerTetheringEventCallback(ITetheringEventCallback callback,
String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.registerTetheringEventCallback(callback);
+ mTetheringManager.registerTetheringEventCallback(callback);
}
/** Unregister tethering event callback. */
@@ -4173,7 +4158,7 @@
public void unregisterTetheringEventCallback(ITetheringEventCallback callback,
String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
- mTethering.unregisterTetheringEventCallback(callback);
+ mTetheringManager.unregisterTetheringEventCallback(callback);
}
// Called when we lose the default network and have no replacement yet.
@@ -5647,7 +5632,8 @@
// are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
+ updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
+ networkAgent.networkInfo.getType());
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
@@ -5726,21 +5712,26 @@
}
- private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
- NetworkCapabilities caps) {
- CompareResult<String> interfaceDiff = new CompareResult<>(
+ private void updateInterfaces(final @Nullable LinkProperties newLp,
+ final @Nullable LinkProperties oldLp, final int netId,
+ final @Nullable NetworkCapabilities caps, final int legacyType) {
+ final CompareResult<String> interfaceDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllInterfaceNames() : null,
newLp != null ? newLp.getAllInterfaceNames() : null);
- for (String iface : interfaceDiff.added) {
- try {
- if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNMS.addInterfaceToNetwork(iface, netId);
- wakeupModifyInterface(iface, caps, true);
- } catch (Exception e) {
- loge("Exception adding interface: " + e);
+ if (!interfaceDiff.added.isEmpty()) {
+ final IBatteryStats bs = mDeps.getBatteryStatsService();
+ for (final String iface : interfaceDiff.added) {
+ try {
+ if (DBG) log("Adding iface " + iface + " to network " + netId);
+ mNMS.addInterfaceToNetwork(iface, netId);
+ wakeupModifyInterface(iface, caps, true);
+ bs.noteNetworkInterfaceType(iface, legacyType);
+ } catch (Exception e) {
+ loge("Exception adding interface: " + e);
+ }
}
}
- for (String iface : interfaceDiff.removed) {
+ for (final String iface : interfaceDiff.removed) {
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
@@ -6476,77 +6467,6 @@
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
-
- // Linger any networks that are no longer needed. This should be done after sending the
- // available callback for newNetwork.
- for (NetworkAgentInfo nai : removedRequests) {
- updateLingerState(nai, now);
- }
- // Possibly unlinger newNetwork. Unlingering a network does not send any callbacks so it
- // does not need to be done in any particular order.
- updateLingerState(newNetwork, now);
-
- if (isNewDefault) {
- // Maintain the illusion: since the legacy API only
- // understands one network at a time, we must pretend
- // that the current default network disconnected before
- // the new one connected.
- if (oldDefaultNetwork != null) {
- mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
- oldDefaultNetwork, true);
- }
- mDefaultInetConditionPublished = newNetwork.lastValidated ? 100 : 0;
- mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
- notifyLockdownVpn(newNetwork);
- }
-
- if (reassignedRequests.containsValue(newNetwork) || newNetwork.isVPN()) {
- // Notify battery stats service about this network, both the normal
- // interface and any stacked links.
- // TODO: Avoid redoing this; this must only be done once when a network comes online.
- try {
- final IBatteryStats bs = BatteryStatsService.getService();
- final int type = newNetwork.networkInfo.getType();
-
- final String baseIface = newNetwork.linkProperties.getInterfaceName();
- bs.noteNetworkInterfaceType(baseIface, type);
- for (LinkProperties stacked : newNetwork.linkProperties.getStackedLinks()) {
- final String stackedIface = stacked.getInterfaceName();
- bs.noteNetworkInterfaceType(stackedIface, type);
- }
- } catch (RemoteException ignored) {
- }
-
- // This has to happen after the notifyNetworkCallbacks as that tickles each
- // ConnectivityManager instance so that legacy requests correctly bind dns
- // requests to this network. The legacy users are listening for this broadcast
- // and will generally do a dns request so they can ensureRouteToHost and if
- // they do that before the callbacks happen they'll use the default network.
- //
- // TODO: Is there still a race here? We send the broadcast
- // after sending the callback, but if the app can receive the
- // broadcast before the callback, it might still break.
- //
- // This *does* introduce a race where if the user uses the new api
- // (notification callbacks) and then uses the old api (getNetworkInfo(type))
- // they may get old info. Reverse this after the old startUsing api is removed.
- // This is on top of the multiple intent sequencing referenced in the todo above.
- for (int i = 0; i < newNetwork.numNetworkRequests(); i++) {
- NetworkRequest nr = newNetwork.requestAt(i);
- if (nr.legacyType != TYPE_NONE && nr.isRequest()) {
- // legacy type tracker filters out repeat adds
- mLegacyTypeTracker.add(nr.legacyType, newNetwork);
- }
- }
-
- // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
- // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
- // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
- // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
- if (newNetwork.isVPN()) {
- mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
- }
- }
}
/**
@@ -6560,15 +6480,32 @@
// requests. Once the code has switched to a request-major iteration style, this can
// be optimized to only do the processing needed.
final long now = SystemClock.elapsedRealtime();
+ final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+
final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
new NetworkAgentInfo[mNetworkAgentInfos.size()]);
// Rematch higher scoring networks first to prevent requests first matching a lower
// scoring network and then a higher scoring network, which could produce multiple
- // callbacks and inadvertently unlinger networks.
+ // callbacks.
Arrays.sort(nais);
- for (NetworkAgentInfo nai : nais) {
+ for (final NetworkAgentInfo nai : nais) {
rematchNetworkAndRequests(nai, now);
}
+
+ final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork();
+
+ for (final NetworkAgentInfo nai : nais) {
+ // Rematching may have altered the linger state of some networks, so update all linger
+ // timers. updateLingerState reads the state from the network agent and does nothing
+ // if the state has not changed : the source of truth is controlled with
+ // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
+ // called while rematching the individual networks above.
+ updateLingerState(nai, now);
+ }
+
+ updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
+
+ // Tear down all unneeded networks.
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
if (unneeded(nai, UnneededFor.TEARDOWN)) {
if (nai.getLingerExpiry() > 0) {
@@ -6588,6 +6525,70 @@
}
}
+ private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
+ @Nullable final NetworkAgentInfo oldDefaultNetwork,
+ @Nullable final NetworkAgentInfo newDefaultNetwork,
+ @NonNull final NetworkAgentInfo[] nais) {
+ if (oldDefaultNetwork != newDefaultNetwork) {
+ // Maintain the illusion : since the legacy API only understands one network at a time,
+ // if the default network changed, apps should see a disconnected broadcast for the
+ // old default network before they see a connected broadcast for the new one.
+ if (oldDefaultNetwork != null) {
+ mLegacyTypeTracker.remove(oldDefaultNetwork.networkInfo.getType(),
+ oldDefaultNetwork, true);
+ }
+ if (newDefaultNetwork != null) {
+ // The new default network can be newly null if and only if the old default
+ // network doesn't satisfy the default request any more because it lost a
+ // capability.
+ mDefaultInetConditionPublished = newDefaultNetwork.lastValidated ? 100 : 0;
+ mLegacyTypeTracker.add(newDefaultNetwork.networkInfo.getType(), newDefaultNetwork);
+ // If the legacy VPN is connected, notifyLockdownVpn may end up sending a broadcast
+ // to reflect the NetworkInfo of this new network. This broadcast has to be sent
+ // after the disconnect broadcasts above, but before the broadcasts sent by the
+ // legacy type tracker below.
+ // TODO : refactor this, it's too complex
+ notifyLockdownVpn(newDefaultNetwork);
+ }
+ }
+
+ // Now that all the callbacks have been sent, send the legacy network broadcasts
+ // as needed. This is necessary so that legacy requests correctly bind dns
+ // requests to this network. The legacy users are listening for this broadcast
+ // and will generally do a dns request so they can ensureRouteToHost and if
+ // they do that before the callbacks happen they'll use the default network.
+ //
+ // TODO: Is there still a race here? The legacy broadcast will be sent after sending
+ // callbacks, but if apps can receive the broadcast before the callback, they still might
+ // have an inconsistent view of networking.
+ //
+ // This *does* introduce a race where if the user uses the new api
+ // (notification callbacks) and then uses the old api (getNetworkInfo(type))
+ // they may get old info. Reverse this after the old startUsing api is removed.
+ // This is on top of the multiple intent sequencing referenced in the todo above.
+ for (NetworkAgentInfo nai : nais) {
+ addNetworkToLegacyTypeTracker(nai);
+ }
+ }
+
+ private void addNetworkToLegacyTypeTracker(@NonNull final NetworkAgentInfo nai) {
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ if (nr.legacyType != TYPE_NONE && nr.isRequest()) {
+ // legacy type tracker filters out repeat adds
+ mLegacyTypeTracker.add(nr.legacyType, nai);
+ }
+ }
+
+ // A VPN generally won't get added to the legacy tracker in the "for (nri)" loop above,
+ // because usually there are no NetworkRequests it satisfies (e.g., mDefaultRequest
+ // wants the NOT_VPN capability, so it will never be satisfied by a VPN). So, add the
+ // newNetwork to the tracker explicitly (it's a no-op if it has already been added).
+ if (nai.isVPN()) {
+ mLegacyTypeTracker.add(TYPE_VPN, nai);
+ }
+ }
+
private void updateInetCondition(NetworkAgentInfo nai) {
// Don't bother updating until we've graduated to validated at least once.
if (!nai.everValidated) return;
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9ebe896..5f562cf 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -146,8 +146,13 @@
@Override
public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
- mService.systemRunning();
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // the location service must be functioning after this boot phase
+ mService.onSystemReady();
+ } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+ // some providers rely on third party code, so we wait to initialize
+ // providers until third party code is allowed to run
+ mService.onSystemThirdPartyAppsCanStart();
}
}
}
@@ -264,156 +269,159 @@
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationExtraPackageNames));
- // most startup is deferred until systemRunning()
+ // most startup is deferred until systemReady()
}
- private void systemRunning() {
+ private void onSystemReady() {
synchronized (mLock) {
- initializeLocked();
+ mPackageManager = mContext.getPackageManager();
+ mAppOps = mContext.getSystemService(AppOpsManager.class);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
+ mUserManager = mContext.getSystemService(UserManager.class);
+
+ mSettingsStore = new LocationSettingsStore(mContext, mHandler);
+ mLocationFudger = new LocationFudger(mContext, mHandler);
+ mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
+
+ PowerManagerInternal localPowerManager =
+ LocalServices.getService(PowerManagerInternal.class);
+
+ // add listeners
+ mAppOps.startWatchingMode(
+ AppOpsManager.OP_COARSE_LOCATION,
+ null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES,
+ new AppOpsManager.OnOpChangedInternalListener() {
+ public void onOpChanged(int op, String packageName) {
+ // onOpChanged invoked on ui thread, move to our thread to reduce
+ // risk of
+ // blocking ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onAppOpChangedLocked();
+ }
+ });
+ }
+ });
+ mPackageManager.addOnPermissionsChangeListener(
+ uid -> {
+ // listener invoked on ui thread, move to our thread to reduce risk of
+ // blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onPermissionsChangedLocked();
+ }
+ });
+ });
+ mActivityManager.addOnUidImportanceListener(
+ (uid, importance) -> {
+ // listener invoked on ui thread, move to our thread to reduce risk of
+ // blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onUidImportanceChangedLocked(uid, importance);
+ }
+ });
+ },
+ FOREGROUND_IMPORTANCE_CUTOFF);
+
+ localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
+ state -> {
+ // listener invoked on ui thread, move to our thread to reduce risk of
+ // blocking
+ // ui thread
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ onBatterySaverModeChangedLocked(state.locationMode);
+ }
+ });
+ });
+ mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
+
+ mSettingsStore.addOnLocationEnabledChangedListener(() -> {
+ synchronized (mLock) {
+ onLocationModeChangedLocked(true);
+ }
+ });
+ mSettingsStore.addOnLocationProvidersAllowedChangedListener(() -> {
+ synchronized (mLock) {
+ onProviderAllowedChangedLocked();
+ }
+ });
+ mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
+ synchronized (mLock) {
+ onBackgroundThrottleIntervalChangedLocked();
+ }
+ });
+ mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
+ synchronized (mLock) {
+ onBackgroundThrottleWhitelistChangedLocked();
+ }
+ });
+ mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
+ synchronized (mLock) {
+ onIgnoreSettingsWhitelistChangedLocked();
+ }
+ });
+
+ new PackageMonitor() {
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ synchronized (mLock) {
+ LocationManagerService.this.onPackageDisappearedLocked(packageName);
+ }
+ }
+ }.register(mContext, mHandler.getLooper(), true);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+ intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+ intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+
+ mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ synchronized (mLock) {
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED:
+ onUserChangedLocked(
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ break;
+ case Intent.ACTION_MANAGED_PROFILE_ADDED:
+ case Intent.ACTION_MANAGED_PROFILE_REMOVED:
+ onUserProfilesChangedLocked();
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ case Intent.ACTION_SCREEN_OFF:
+ onScreenStateChangedLocked();
+ break;
+ }
+ }
+ }
+ }, UserHandle.ALL, intentFilter, null, mHandler);
+
+ // switching the user from null to system here performs the bulk of the initialization
+ // work. the user being changed will cause a reload of all user specific settings, which
+ // causes initialization, and propagates changes until a steady state is reached
+ mCurrentUserId = UserHandle.USER_NULL;
+ onUserChangedLocked(ActivityManager.getCurrentUser());
}
}
- @GuardedBy("mLock")
- private void initializeLocked() {
- mPackageManager = mContext.getPackageManager();
- mAppOps = mContext.getSystemService(AppOpsManager.class);
- mPowerManager = mContext.getSystemService(PowerManager.class);
- mActivityManager = mContext.getSystemService(ActivityManager.class);
- mUserManager = mContext.getSystemService(UserManager.class);
-
- mSettingsStore = new LocationSettingsStore(mContext, mHandler);
-
- mLocationFudger = new LocationFudger(mContext, mHandler);
- mGeofenceManager = new GeofenceManager(mContext, mSettingsStore);
-
- PowerManagerInternal localPowerManager =
- LocalServices.getService(PowerManagerInternal.class);
-
- // prepare providers
- initializeProvidersLocked();
-
- // add listeners
- mAppOps.startWatchingMode(
- AppOpsManager.OP_COARSE_LOCATION,
- null,
- AppOpsManager.WATCH_FOREGROUND_CHANGES,
- new AppOpsManager.OnOpChangedInternalListener() {
- public void onOpChanged(int op, String packageName) {
- // onOpChanged invoked on ui thread, move to our thread to reduce risk of
- // blocking ui thread
- mHandler.post(() -> {
- synchronized (mLock) {
- onAppOpChangedLocked();
- }
- });
- }
- });
- mPackageManager.addOnPermissionsChangeListener(
- uid -> {
- // listener invoked on ui thread, move to our thread to reduce risk of blocking
- // ui thread
- mHandler.post(() -> {
- synchronized (mLock) {
- onPermissionsChangedLocked();
- }
- });
- });
- mActivityManager.addOnUidImportanceListener(
- (uid, importance) -> {
- // listener invoked on ui thread, move to our thread to reduce risk of blocking
- // ui thread
- mHandler.post(() -> {
- synchronized (mLock) {
- onUidImportanceChangedLocked(uid, importance);
- }
- });
- },
- FOREGROUND_IMPORTANCE_CUTOFF);
-
- localPowerManager.registerLowPowerModeObserver(ServiceType.LOCATION,
- state -> {
- // listener invoked on ui thread, move to our thread to reduce risk of blocking
- // ui thread
- mHandler.post(() -> {
- synchronized (mLock) {
- onBatterySaverModeChangedLocked(state.locationMode);
- }
- });
- });
- mBatterySaverMode = mPowerManager.getLocationPowerSaveMode();
-
- mSettingsStore.addOnLocationEnabledChangedListener(() -> {
- synchronized (mLock) {
- onLocationModeChangedLocked(true);
- }
- });
- mSettingsStore.addOnLocationProvidersAllowedChangedListener(() -> {
- synchronized (mLock) {
- onProviderAllowedChangedLocked();
- }
- });
- mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
- synchronized (mLock) {
- onBackgroundThrottleIntervalChangedLocked();
- }
- });
- mSettingsStore.addOnBackgroundThrottlePackageWhitelistChangedListener(() -> {
- synchronized (mLock) {
- onBackgroundThrottleWhitelistChangedLocked();
- }
- });
- mSettingsStore.addOnIgnoreSettingsPackageWhitelistChangedListener(() -> {
- synchronized (mLock) {
- onIgnoreSettingsWhitelistChangedLocked();
- }
- });
-
- new PackageMonitor() {
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- synchronized (mLock) {
- LocationManagerService.this.onPackageDisappearedLocked(packageName);
- }
- }
- }.register(mContext, mHandler.getLooper(), true);
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
- intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
- intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action == null) {
- return;
- }
- synchronized (mLock) {
- switch (action) {
- case Intent.ACTION_USER_SWITCHED:
- onUserChangedLocked(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- break;
- case Intent.ACTION_MANAGED_PROFILE_ADDED:
- case Intent.ACTION_MANAGED_PROFILE_REMOVED:
- onUserProfilesChangedLocked();
- break;
- case Intent.ACTION_SCREEN_ON:
- case Intent.ACTION_SCREEN_OFF:
- onScreenStateChangedLocked();
- break;
- }
- }
- }
- }, UserHandle.ALL, intentFilter, null, mHandler);
-
- // switching the user from null to system here performs the bulk of the initialization work.
- // the user being changed will cause a reload of all user specific settings, which causes
- // provider initialization, and propagates changes until a steady state is reached
- mCurrentUserId = UserHandle.USER_NULL;
- onUserChangedLocked(ActivityManager.getCurrentUser());
+ private void onSystemThirdPartyAppsCanStart() {
+ synchronized (mLock) {
+ // prepare providers
+ initializeProvidersLocked();
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index bc50956..e2fe76e 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -247,7 +247,14 @@
List<MonitoredPackage> packages = new ArrayList<>();
for (int i = 0; i < packageNames.size(); i++) {
// Health checks not available yet so health check state will start INACTIVE
- packages.add(new MonitoredPackage(packageNames.get(i), durationMs, false));
+ MonitoredPackage pkg = newMonitoredPackage(packageNames.get(i), durationMs, false);
+ if (pkg != null) {
+ packages.add(pkg);
+ }
+ }
+
+ if (packages.isEmpty()) {
+ return;
}
// Sync before we add the new packages to the observers. This will #pruneObservers,
@@ -634,16 +641,8 @@
if (registeredObserver != null) {
Iterator<MonitoredPackage> it = failedPackages.iterator();
while (it.hasNext()) {
- String failedPackage = it.next().getName();
- Slog.i(TAG, "Explicit health check failed for package " + failedPackage);
- VersionedPackage versionedPkg = getVersionedPackage(failedPackage);
- if (versionedPkg == null) {
- Slog.w(TAG, "Explicit health check failed but could not find package "
- + failedPackage);
- // TODO(b/120598832): Skip. We only continue to pass tests for now since
- // the tests don't install any packages
- versionedPkg = new VersionedPackage(failedPackage, 0L);
- }
+ VersionedPackage versionedPkg = it.next().mPackage;
+ Slog.i(TAG, "Explicit health check failed for package " + versionedPkg);
registeredObserver.execute(versionedPkg);
}
}
@@ -654,7 +653,7 @@
@Nullable
private VersionedPackage getVersionedPackage(String packageName) {
final PackageManager pm = mContext.getPackageManager();
- if (pm == null) {
+ if (pm == null || TextUtils.isEmpty(packageName)) {
return null;
}
try {
@@ -848,7 +847,7 @@
public void updatePackagesLocked(List<MonitoredPackage> packages) {
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
MonitoredPackage p = packages.get(pIndex);
- this.packages.put(p.mName, p);
+ this.packages.put(p.getName(), p);
}
}
@@ -872,7 +871,7 @@
int newState = p.handleElapsedTimeLocked(elapsedMs);
if (oldState != HealthCheckState.FAILED
&& newState == HealthCheckState.FAILED) {
- Slog.i(TAG, "Package " + p.mName + " failed health check");
+ Slog.i(TAG, "Package " + p.getName() + " failed health check");
failedPackages.add(p);
}
if (p.isExpiredLocked()) {
@@ -925,9 +924,10 @@
ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
boolean hasPassedHealthCheck = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK));
- if (!TextUtils.isEmpty(packageName)) {
- packages.add(watchdog.new MonitoredPackage(packageName, duration,
- healthCheckDuration, hasPassedHealthCheck));
+ MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName,
+ duration, healthCheckDuration, hasPassedHealthCheck);
+ if (pkg != null) {
+ packages.add(pkg);
}
} catch (NumberFormatException e) {
Slog.wtf(TAG, "Skipping package for observer " + observerName, e);
@@ -963,6 +963,20 @@
int FAILED = 3;
}
+ MonitoredPackage newMonitoredPackage(
+ String name, long durationMs, boolean hasPassedHealthCheck) {
+ return newMonitoredPackage(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
+ }
+
+ MonitoredPackage newMonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
+ boolean hasPassedHealthCheck) {
+ VersionedPackage pkg = getVersionedPackage(name);
+ if (pkg == null) {
+ return null;
+ }
+ return new MonitoredPackage(pkg, durationMs, healthCheckDurationMs, hasPassedHealthCheck);
+ }
+
/**
* Represents a package and its health check state along with the time
* it should be monitored for.
@@ -971,8 +985,7 @@
* instances of this class.
*/
class MonitoredPackage {
- //TODO(b/120598832): VersionedPackage?
- private final String mName;
+ private final VersionedPackage mPackage;
// Times when package failures happen sorted in ascending order
@GuardedBy("mLock")
private final LongArrayQueue mFailureHistory = new LongArrayQueue();
@@ -996,13 +1009,9 @@
@GuardedBy("mLock")
private long mHealthCheckDurationMs = Long.MAX_VALUE;
- MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) {
- this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
- }
-
- MonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
- boolean hasPassedHealthCheck) {
- mName = name;
+ private MonitoredPackage(VersionedPackage pkg, long durationMs,
+ long healthCheckDurationMs, boolean hasPassedHealthCheck) {
+ mPackage = pkg;
mDurationMs = durationMs;
mHealthCheckDurationMs = healthCheckDurationMs;
mHasPassedHealthCheck = hasPassedHealthCheck;
@@ -1013,7 +1022,7 @@
@GuardedBy("mLock")
public void writeLocked(XmlSerializer out) throws IOException {
out.startTag(null, TAG_PACKAGE);
- out.attribute(null, ATTR_NAME, mName);
+ out.attribute(null, ATTR_NAME, getName());
out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs));
out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
String.valueOf(mHealthCheckDurationMs));
@@ -1053,7 +1062,7 @@
public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) {
if (initialHealthCheckDurationMs <= 0) {
Slog.wtf(TAG, "Cannot set non-positive health check duration "
- + initialHealthCheckDurationMs + "ms for package " + mName
+ + initialHealthCheckDurationMs + "ms for package " + getName()
+ ". Using total duration " + mDurationMs + "ms instead");
initialHealthCheckDurationMs = mDurationMs;
}
@@ -1072,7 +1081,7 @@
@GuardedBy("mLock")
public int handleElapsedTimeLocked(long elapsedMs) {
if (elapsedMs <= 0) {
- Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + mName);
+ Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName());
return mHealthCheckState;
}
// Transitions to FAILED if now <= 0 and health check not passed
@@ -1105,7 +1114,7 @@
/** Returns the monitored package name. */
private String getName() {
- return mName;
+ return mPackage.getPackageName();
}
/**
@@ -1170,7 +1179,7 @@
} else {
mHealthCheckState = HealthCheckState.ACTIVE;
}
- Slog.i(TAG, "Updated health check state for package " + mName + ": "
+ Slog.i(TAG, "Updated health check state for package " + getName() + ": "
+ toString(oldState) + " -> " + toString(mHealthCheckState));
return mHealthCheckState;
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a4bc3bd..9d1eb6c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -118,6 +118,7 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.DataUnit;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
@@ -819,7 +820,6 @@
refreshFuseSettings();
});
refreshIsolatedStorageSettings();
- refreshFuseSettings();
}
/**
@@ -883,16 +883,25 @@
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res));
}
+ /**
+ * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is
+ * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse)
+ * whenever the user reboots.
+ */
private void refreshFuseSettings() {
int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
FUSE_ENABLED, 0);
if (isFuseEnabled == 1) {
- SystemProperties.set(StorageManager.PROP_FUSE, "true");
+ Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on");
+ SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
+ + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true");
} else if (isFuseEnabled == -1) {
- SystemProperties.set(StorageManager.PROP_FUSE, "false");
+ Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off");
+ SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX
+ + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false");
}
// else, keep the build config.
- // This can be overridden be direct adjustment of persist.sys.prop
+ // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse
}
/**
@@ -1548,6 +1557,8 @@
public StorageManagerService(Context context) {
sSelf = this;
+ updateFusePropFromSettings();
+
// Snapshot feature flag used for this boot
SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
@@ -1609,6 +1620,23 @@
}
}
+ /**
+ * Checks if user changed the persistent settings_fuse flag from Settings UI
+ * and updates PROP_FUSE (reboots if changed).
+ */
+ private void updateFusePropFromSettings() {
+ Boolean settingsFuseFlag = SystemProperties.getBoolean((FeatureFlagUtils.PERSIST_PREFIX
+ + FeatureFlagUtils.SETTINGS_FUSE_FLAG), false);
+ Slog.d(TAG, "The value of Settings Fuse Flag is " + settingsFuseFlag);
+ if (SystemProperties.getBoolean(StorageManager.PROP_FUSE, false) != settingsFuseFlag) {
+ Slog.d(TAG, "Set persist.sys.fuse to " + settingsFuseFlag);
+ SystemProperties.set(StorageManager.PROP_FUSE, Boolean.toString(settingsFuseFlag));
+ // Perform hard reboot to kick policy into place
+ mContext.getSystemService(PowerManager.class).reboot("Reboot device for FUSE system"
+ + "property change to take effect");
+ }
+ }
+
private void start() {
connectStoraged();
connectVold();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e119d1e..5b8ffde 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3784,7 +3784,7 @@
}
@GuardedBy("this")
- void waitForProcKillLocked(final ProcessRecord app, final String formatString,
+ private void waitForProcKillLocked(final ProcessRecord app, final String formatString,
final long startTime) {
app.mAppDiedCallback = () -> {
synchronized (ActivityManagerService.this) {
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 0524f91..638111e 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -126,7 +126,7 @@
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mWorkerLock")
private WifiActivityEnergyInfo mLastInfo =
- new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
+ new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0);
/**
* Timestamp at which all external stats were last collected in
@@ -548,42 +548,45 @@
@GuardedBy("mWorkerLock")
private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
- final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
- final long lastScanMs = mLastInfo.mControllerScanTimeMs;
- final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
- final long lastTxMs = mLastInfo.mControllerTxTimeMs;
- final long lastRxMs = mLastInfo.mControllerRxTimeMs;
- final long lastEnergy = mLastInfo.mControllerEnergyUsed;
+ final long timePeriodMs = latest.getTimeSinceBootMillis()
+ - mLastInfo.getTimeSinceBootMillis();
+ final long lastScanMs = mLastInfo.getControllerScanDurationMillis();
+ final long lastIdleMs = mLastInfo.getControllerIdleDurationMillis();
+ final long lastTxMs = mLastInfo.getControllerTxDurationMillis();
+ final long lastRxMs = mLastInfo.getControllerRxDurationMillis();
+ final long lastEnergy = mLastInfo.getControllerEnergyUsedMicroJoules();
// We will modify the last info object to be the delta, and store the new
// WifiActivityEnergyInfo object as our last one.
final WifiActivityEnergyInfo delta = mLastInfo;
- delta.mTimestamp = latest.getTimeStamp();
- delta.mStackState = latest.getStackState();
+ delta.setTimeSinceBootMillis(latest.getTimeSinceBootMillis());
+ delta.setStackState(latest.getStackState());
- final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
- final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
- final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
- final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
+ final long txTimeMs = latest.getControllerTxDurationMillis() - lastTxMs;
+ final long rxTimeMs = latest.getControllerRxDurationMillis() - lastRxMs;
+ final long idleTimeMs = latest.getControllerIdleDurationMillis() - lastIdleMs;
+ final long scanTimeMs = latest.getControllerScanDurationMillis() - lastScanMs;
if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0 || idleTimeMs < 0) {
// The stats were reset by the WiFi system (which is why our delta is negative).
// Returns the unaltered stats. The total on time should not exceed the time
// duartion between reports.
- final long totalOnTimeMs = latest.mControllerTxTimeMs + latest.mControllerRxTimeMs
- + latest.mControllerIdleTimeMs;
+ final long totalOnTimeMs = latest.getControllerTxDurationMillis()
+ + latest.getControllerRxDurationMillis()
+ + latest.getControllerIdleDurationMillis();
if (totalOnTimeMs <= timePeriodMs + MAX_WIFI_STATS_SAMPLE_ERROR_MILLIS) {
- delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
- delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
- delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
- delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
- delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
+ delta.setControllerEnergyUsedMicroJoules(
+ latest.getControllerEnergyUsedMicroJoules());
+ delta.setControllerRxDurationMillis(latest.getControllerRxDurationMillis());
+ delta.setControllerTxDurationMillis(latest.getControllerTxDurationMillis());
+ delta.setControllerIdleDurationMillis(latest.getControllerIdleDurationMillis());
+ delta.setControllerScanDurationMillis(latest.getControllerScanDurationMillis());
} else {
- delta.mControllerEnergyUsed = 0;
- delta.mControllerRxTimeMs = 0;
- delta.mControllerTxTimeMs = 0;
- delta.mControllerIdleTimeMs = 0;
- delta.mControllerScanTimeMs = 0;
+ delta.setControllerEnergyUsedMicroJoules(0);
+ delta.setControllerRxDurationMillis(0);
+ delta.setControllerTxDurationMillis(0);
+ delta.setControllerIdleDurationMillis(0);
+ delta.setControllerScanDurationMillis(0);
}
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
} else {
@@ -608,28 +611,30 @@
sb.append(" e=").append(lastEnergy);
sb.append("\n");
sb.append("Current WiFi snapshot: ").append("idle=");
- TimeUtils.formatDuration(latest.mControllerIdleTimeMs, sb);
+ TimeUtils.formatDuration(latest.getControllerIdleDurationMillis(), sb);
sb.append(" rx=");
- TimeUtils.formatDuration(latest.mControllerRxTimeMs, sb);
+ TimeUtils.formatDuration(latest.getControllerRxDurationMillis(), sb);
sb.append(" tx=");
- TimeUtils.formatDuration(latest.mControllerTxTimeMs, sb);
- sb.append(" e=").append(latest.mControllerEnergyUsed);
+ TimeUtils.formatDuration(latest.getControllerTxDurationMillis(), sb);
+ sb.append(" e=").append(latest.getControllerEnergyUsedMicroJoules());
Slog.wtf(TAG, sb.toString());
}
} else {
maxExpectedIdleTimeMs = timePeriodMs - totalActiveTimeMs;
}
// These times seem to be the most reliable.
- delta.mControllerTxTimeMs = txTimeMs;
- delta.mControllerRxTimeMs = rxTimeMs;
- delta.mControllerScanTimeMs = scanTimeMs;
+ delta.setControllerTxDurationMillis(txTimeMs);
+ delta.setControllerRxDurationMillis(rxTimeMs);
+ delta.setControllerScanDurationMillis(scanTimeMs);
// WiFi calculates the idle time as a difference from the on time and the various
// Rx + Tx times. There seems to be some missing time there because this sometimes
// becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
// time from the difference in timestamps.
// b/21613534
- delta.mControllerIdleTimeMs = Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs));
- delta.mControllerEnergyUsed = Math.max(0, latest.mControllerEnergyUsed - lastEnergy);
+ delta.setControllerIdleDurationMillis(
+ Math.min(maxExpectedIdleTimeMs, Math.max(0, idleTimeMs)));
+ delta.setControllerEnergyUsedMicroJoules(
+ Math.max(0, latest.getControllerEnergyUsedMicroJoules() - lastEnergy));
}
mLastInfo = latest;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 375da8a..2bb7035 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1421,7 +1421,7 @@
if (app.pendingStart) {
return true;
}
- long startTime = SystemClock.uptimeMillis();
+ long startTime = SystemClock.elapsedRealtime();
if (app.pid > 0 && app.pid != ActivityManagerService.MY_PID) {
checkSlow(startTime, "startProcess: removing from pids map");
mService.mPidsSelfLocked.remove(app);
@@ -1856,7 +1856,7 @@
boolean knownToBeDead, int intentFlags, HostingRecord hostingRecord,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
String abiOverride, String entryPoint, String[] entryPointArgs, Runnable crashHandler) {
- long startTime = SystemClock.uptimeMillis();
+ long startTime = SystemClock.elapsedRealtime();
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(processName, info.uid, keepIfLarge);
@@ -1917,9 +1917,10 @@
// An application record is attached to a previous process,
// clean it up now.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
+ checkSlow(startTime, "startProcess: bad proc running, killing");
ProcessList.killProcessGroup(app.uid, app.pid);
- mService.waitForProcKillLocked(app, "startProcess: bad proc running, killing: %s",
- startTime);
+ mService.handleAppDiedLocked(app, true, true);
+ checkSlow(startTime, "startProcess: done killing old proc");
}
if (app == null) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 7e9a17b..366766e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1115,6 +1115,7 @@
// There is some actively running operation... need to find it
// and appropriately update its state.
final long now = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
for (int i = uidState.pkgOps.size() - 1; i >= 0; i--) {
final Ops ops = uidState.pkgOps.valueAt(i);
for (int j = ops.size() - 1; j >= 0; j--) {
@@ -1136,7 +1137,7 @@
featureOp.finished(now, duration, oldPendingState,
AppOpsManager.OP_FLAG_SELF);
// Start the op in the new state
- featureOp.startRealtime = now;
+ featureOp.startRealtime = nowElapsed;
featureOp.started(now, newState, AppOpsManager.OP_FLAG_SELF);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e426c6c..4bf1de6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -269,6 +269,7 @@
private static final int MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS = 27;
private static final int MSG_HDMI_VOLUME_CHECK = 28;
private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
+ private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -2925,9 +2926,8 @@
AudioSystem.muteMicrophone(muted);
try {
if (muted != currentMute) {
- mContext.sendBroadcastAsUser(
- new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
- .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL);
+ sendMsg(mAudioHandler, MSG_BROADCAST_MICROPHONE_MUTE,
+ SENDMSG_NOOP, 0, 0, null, 0);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -5236,6 +5236,13 @@
case MSG_PLAYBACK_CONFIG_CHANGE:
onPlaybackConfigChange((List<AudioPlaybackConfiguration>) msg.obj);
break;
+
+ case MSG_BROADCAST_MICROPHONE_MUTE:
+ mContext.sendBroadcastAsUser(
+ new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 24a5b7f..bb7f862 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -580,7 +580,7 @@
// semantics of WakeupMessage guarantee that if cancel is called then the alarm will
// never call its callback (handleLingerComplete), even if it has already fired.
// WakeupMessage makes no such guarantees about rescheduling a message, so if mLingerMessage
- // has already been dispatched, rescheduling to some time in the future it won't stop it
+ // has already been dispatched, rescheduling to some time in the future won't stop it
// from calling its callback immediately.
if (mLingerMessage != null) {
mLingerMessage.cancel();
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
deleted file mode 100644
index 4ad7ac4..0000000
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ /dev/null
@@ -1,96 +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.connectivity.tethering;
-
-import android.content.Context;
-import android.net.NetworkRequest;
-import android.net.ip.IpServer;
-import android.net.util.SharedLog;
-import android.os.Handler;
-
-import com.android.internal.util.StateMachine;
-import com.android.server.connectivity.MockableSystemProperties;
-
-import java.util.ArrayList;
-
-
-/**
- * Capture tethering dependencies, for injection.
- *
- * @hide
- */
-public class TetheringDependencies {
- /**
- * Get a reference to the offload hardware interface to be used by tethering.
- */
- public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
- return new OffloadHardwareInterface(h, log);
- }
-
- /**
- * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
- */
- public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
- SharedLog log, int what) {
- return new UpstreamNetworkMonitor(ctx, target, log, what);
- }
-
- /**
- * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
- */
- public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
- ArrayList<IpServer> notifyList, SharedLog log) {
- return new IPv6TetheringCoordinator(notifyList, log);
- }
-
- /**
- * Get dependencies to be used by IpServer.
- */
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies();
- }
-
- /**
- * Indicates whether tethering is supported on the device.
- */
- public boolean isTetheringSupported() {
- return true;
- }
-
- /**
- * Get the NetworkRequest that should be fulfilled by the default network.
- */
- public NetworkRequest getDefaultNetworkRequest() {
- return null;
- }
-
- /**
- * Get a reference to the EntitlementManager to be used by tethering.
- */
- public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
- SharedLog log, int what, MockableSystemProperties systemProperties) {
- return new EntitlementManager(ctx, target, log, what, systemProperties);
- }
-
- /**
- * Generate a new TetheringConfiguration according to input sub Id.
- */
- public TetheringConfiguration generateTetheringConfiguration(Context ctx, SharedLog log,
- int subId) {
- return new TetheringConfiguration(ctx, log, subId);
- }
-}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 9882f6c..7ce63c5 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -138,13 +138,17 @@
}
/**
- * Sets the display modes the system is allowed to switch between, roughly ordered by
- * preference.
+ * Sets the refresh ranges, and display modes that the system is allowed to switch between.
+ * Display modes are roughly ordered by preference.
*
* Not all display devices will automatically switch between modes, so it's important that the
* most-desired modes are at the beginning of the allowed array.
+ *
+ * @param defaultModeId is used, if the device does not support multiple refresh
+ * rates, and to navigate other parameters.
*/
- public void setAllowedDisplayModesLocked(int[] modes) {
+ public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+ float maxRefreshRate, int[] modes) {
}
/**
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index d24bd1a..6118df5 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -18,26 +18,22 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.UserHandle;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.TextUtils;
@@ -47,11 +43,11 @@
import android.view.Display;
import android.view.DisplayInfo;
-import com.android.internal.os.BackgroundThread;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
-import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -87,11 +83,11 @@
// A map from the display ID to the collection of votes and their priority. The latter takes
// the form of another map from the priority to the vote itself so that each priority is
// guaranteed to have exactly one vote, which is also easily and efficiently replaceable.
- private final SparseArray<SparseArray<Vote>> mVotesByDisplay;
+ private SparseArray<SparseArray<Vote>> mVotesByDisplay;
// A map from the display ID to the supported modes on that display.
- private final SparseArray<Display.Mode[]> mSupportedModesByDisplay;
+ private SparseArray<Display.Mode[]> mSupportedModesByDisplay;
// A map from the display ID to the default mode of that display.
- private final SparseArray<Display.Mode> mDefaultModeByDisplay;
+ private SparseArray<Display.Mode> mDefaultModeByDisplay;
private final AppRequestObserver mAppRequestObserver;
private final SettingsObserver mSettingsObserver;
@@ -143,17 +139,7 @@
*/
@NonNull
public int[] getAllowedModes(int displayId) {
- synchronized (mLock) {
- SparseArray<Vote> votes = getVotesLocked(displayId);
- Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
- Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
- if (modes == null || defaultMode == null) {
- Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id="
- + displayId + ")");
- return new int[0];
- }
- return getAllowedModesLocked(votes, modes, defaultMode);
- }
+ return getDesiredDisplayConfigSpecs(displayId).allowedConfigs;
}
@NonNull
@@ -178,76 +164,101 @@
return votes;
}
+ /**
+ * Calculates the refresh rate ranges and display modes that the system is allowed to freely
+ * switch between based on global and display-specific constraints.
+ *
+ * @param displayId The display to query for.
+ * @return The ID of the default mode the system should use, and the refresh rate range the
+ * system is allowed to switch between.
+ */
@NonNull
- private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes,
- @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) {
- int lowestConsideredPriority = Vote.MIN_PRIORITY;
- while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) {
+ synchronized (mLock) {
+ SparseArray<Vote> votes = getVotesLocked(displayId);
+ Display.Mode[] modes = mSupportedModesByDisplay.get(displayId);
+ Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
+ if (modes == null || defaultMode == null) {
+ Slog.e(TAG, "Asked about unknown display, returning empty desired configs!"
+ + "(id=" + displayId + ")");
+ return new DesiredDisplayConfigSpecs(displayId, new RefreshRateRange(60, 60),
+ new int[0]);
+ }
+
+ int[] availableModes = new int[]{defaultMode.getModeId()};
float minRefreshRate = 0f;
float maxRefreshRate = Float.POSITIVE_INFINITY;
- int height = Vote.INVALID_SIZE;
- int width = Vote.INVALID_SIZE;
+ int lowestConsideredPriority = Vote.MIN_PRIORITY;
+ while (lowestConsideredPriority <= Vote.MAX_PRIORITY) {
+ int height = Vote.INVALID_SIZE;
+ int width = Vote.INVALID_SIZE;
- for (int priority = Vote.MAX_PRIORITY;
- priority >= lowestConsideredPriority;
- priority--) {
- Vote vote = votes.get(priority);
- if (vote == null) {
- continue;
+ for (int priority = Vote.MAX_PRIORITY;
+ priority >= lowestConsideredPriority; priority--) {
+ Vote vote = votes.get(priority);
+ if (vote == null) {
+ continue;
+ }
+ // For refresh rates, just use the tightest bounds of all the votes
+ minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min);
+ maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max);
+ // For display size, use only the first vote we come across (i.e. the highest
+ // priority vote that includes the width / height).
+ if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
+ && vote.height > 0 && vote.width > 0) {
+ width = vote.width;
+ height = vote.height;
+ }
}
- // For refresh rates, just use the tightest bounds of all the votes
- minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate);
- maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate);
- // For display size, use only the first vote we come across (i.e. the highest
- // priority vote that includes the width / height).
- if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE
- && vote.height > 0 && vote.width > 0) {
- width = vote.width;
- height = vote.height;
+
+ // If we don't have anything specifying the width / height of the display, just use
+ // the default width and height. We don't want these switching out from underneath
+ // us since it's a pretty disruptive behavior.
+ if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
+ width = defaultMode.getPhysicalWidth();
+ height = defaultMode.getPhysicalHeight();
}
- }
- // If we don't have anything specifying the width / height of the display, just use the
- // default width and height. We don't want these switching out from underneath us since
- // it's a pretty disruptive behavior.
- if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) {
- width = defaultMode.getPhysicalWidth();
- height = defaultMode.getPhysicalHeight();
- }
+ availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
+ if (availableModes.length > 0) {
+ if (DEBUG) {
+ Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+ + " with lowest priority considered "
+ + Vote.priorityToString(lowestConsideredPriority)
+ + " and constraints: "
+ + "width=" + width
+ + ", height=" + height
+ + ", minRefreshRate=" + minRefreshRate
+ + ", maxRefreshRate=" + maxRefreshRate);
+ }
+ break;
+ }
- int[] availableModes =
- filterModes(modes, width, height, minRefreshRate, maxRefreshRate);
- if (availableModes.length > 0) {
if (DEBUG) {
- Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
- + " with lowest priority considered "
+ Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
+ Vote.priorityToString(lowestConsideredPriority)
- + " and constraints: "
+ + " and with the following constraints: "
+ "width=" + width
+ ", height=" + height
+ ", minRefreshRate=" + minRefreshRate
+ ", maxRefreshRate=" + maxRefreshRate);
}
- return availableModes;
+
+ // If we haven't found anything with the current set of votes, drop the
+ // current lowest priority vote.
+ lowestConsideredPriority++;
}
- if (DEBUG) {
- Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
- + Vote.priorityToString(lowestConsideredPriority)
- + " and with the following constraints: "
- + "width=" + width
- + ", height=" + height
- + ", minRefreshRate=" + minRefreshRate
- + ", maxRefreshRate=" + maxRefreshRate);
+ int defaultModeId = defaultMode.getModeId();
+ if (availableModes.length > 0) {
+ defaultModeId = availableModes[0];
}
- // If we haven't found anything with the current set of votes, drop the current lowest
- // priority vote.
- lowestConsideredPriority++;
+ // filterModes function is going to filter the modes based on the voting system. If
+ // the application requests a given mode with preferredModeId function, it will be
+ // stored as the first and only element in available modes array.
+ return new DesiredDisplayConfigSpecs(defaultModeId,
+ new RefreshRateRange(minRefreshRate, maxRefreshRate), availableModes);
}
-
- // If we still haven't found anything that matches our current set of votes, just fall back
- // to the default mode.
- return new int[] { defaultMode.getModeId() };
}
private int[] filterModes(Display.Mode[] supportedModes,
@@ -403,6 +414,21 @@
}
}
+ @VisibleForTesting
+ void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) {
+ mSupportedModesByDisplay = supportedModesByDisplay;
+ }
+
+ @VisibleForTesting
+ void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) {
+ mDefaultModeByDisplay = defaultModeByDisplay;
+ }
+
+ @VisibleForTesting
+ void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) {
+ mVotesByDisplay = votesByDisplay;
+ }
+
/**
* Listens for changes to display mode coordination.
*/
@@ -452,7 +478,129 @@
}
}
- private static final class Vote {
+ /**
+ * Information about the min and max refresh rate DM would like to set the display to.
+ */
+ public static final class RefreshRateRange {
+ /**
+ * The lowest desired refresh rate.
+ */
+ public final float min;
+ /**
+ * The highest desired refresh rate.
+ */
+ public final float max;
+
+ public RefreshRateRange(float min, float max) {
+ if (min < 0 || max < 0 || min > max) {
+ Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : "
+ + min + " " + max);
+ this.min = this.max = 0;
+ return;
+ }
+ this.min = min;
+ this.max = max;
+ }
+
+ /**
+ * Checks whether the two objects have the same values.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof RefreshRateRange)) {
+ return false;
+ }
+
+ RefreshRateRange refreshRateRange = (RefreshRateRange) other;
+ return (min == refreshRateRange.min && max == refreshRateRange.max);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(min, max);
+ }
+
+ @Override
+ public String toString() {
+ return "(" + min + " " + max + ")";
+ }
+ }
+
+ /**
+ * Information about the desired configuration to be set by the system. Includes the default
+ * configuration ID, refresh rate range, and the list of policy decisions that influenced the
+ * choice.
+ */
+ public static final class DesiredDisplayConfigSpecs {
+ /**
+ * Default configuration ID. This is what system defaults to for all other settings, or
+ * if the refresh rate range is not available.
+ */
+ public final int defaultModeId;
+ /**
+ * The refresh rate range.
+ */
+ public final RefreshRateRange refreshRateRange;
+ /**
+ * For legacy reasons, keep a list of allowed configs.
+ * TODO(b/142507213): Re-assess whether the list of allowed configs is still necessary.
+ */
+ public final int[] allowedConfigs;
+
+ public DesiredDisplayConfigSpecs(int defaultModeId,
+ @NonNull RefreshRateRange refreshRateRange,
+ @NonNull int[] allowedConfigs) {
+ this.defaultModeId = defaultModeId;
+ this.refreshRateRange = refreshRateRange;
+ this.allowedConfigs = allowedConfigs;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ */
+ @Override
+ public String toString() {
+ return "DesiredDisplayConfigSpecs(defaultModeId=" + defaultModeId
+ + ", refreshRateRange=" + refreshRateRange.toString()
+ + ", allowedConfigs=" + Arrays.toString(allowedConfigs) + ")";
+ }
+ /**
+ * Checks whether the two objects have the same values.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof DesiredDisplayConfigSpecs)) {
+ return false;
+ }
+
+ DesiredDisplayConfigSpecs desiredDisplayConfigSpecs =
+ (DesiredDisplayConfigSpecs) other;
+
+ if (defaultModeId != desiredDisplayConfigSpecs.defaultModeId) {
+ return false;
+ }
+ if (!refreshRateRange.equals(desiredDisplayConfigSpecs.refreshRateRange)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(defaultModeId, refreshRateRange);
+ }
+ }
+
+ @VisibleForTesting
+ static final class Vote {
// LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
// If the higher voters result is a range, it will fix the rate to a single choice.
// It's used to avoid rate switch in certain conditions.
@@ -499,15 +647,10 @@
* The requested height of the display in pixels, or INVALID_SIZE;
*/
public final int height;
-
/**
- * The lowest desired refresh rate.
+ * Information about the min and max refresh rate DM would like to set the display to.
*/
- public final float minRefreshRate;
- /**
- * The highest desired refresh rate.
- */
- public final float maxRefreshRate;
+ public final RefreshRateRange refreshRateRange;
public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) {
return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate);
@@ -521,8 +664,8 @@
float minRefreshRate, float maxRefreshRate) {
this.width = width;
this.height = height;
- this.minRefreshRate = minRefreshRate;
- this.maxRefreshRate = maxRefreshRate;
+ this.refreshRateRange =
+ new RefreshRateRange(minRefreshRate, maxRefreshRate);
}
public static String priorityToString(int priority) {
@@ -547,11 +690,9 @@
@Override
public String toString() {
return "Vote{"
- + "width=" + width
- + ", height=" + height
- + ", minRefreshRate=" + minRefreshRate
- + ", maxRefreshRate=" + maxRefreshRate
- + "}";
+ + "width=" + width + ", height=" + height
+ + ", minRefreshRate=" + refreshRateRange.min
+ + ", maxRefreshRate=" + refreshRateRange.max + "}";
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b03dc3b..1586473 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -173,6 +173,8 @@
private int mActiveModeId;
private boolean mActiveModeInvalid;
private int[] mAllowedModeIds;
+ private float mMinRefreshRate;
+ private float mMaxRefreshRate;
private boolean mAllowedModeIdsInvalid;
private int mActivePhysIndex;
private int[] mAllowedPhysIndexes;
@@ -623,7 +625,9 @@
}
@Override
- public void setAllowedDisplayModesLocked(int[] modes) {
+ public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+ float maxRefreshRate, int[] modes) {
+ updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate);
updateAllowedModesLocked(modes);
}
@@ -653,6 +657,7 @@
return true;
}
+ // TODO(b/142507213): Remove once refresh rates are plummed through to kernel.
public void updateAllowedModesLocked(int[] allowedModes) {
if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) {
return;
@@ -662,6 +667,38 @@
}
}
+ public void updateDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+ float maxRefreshRate) {
+ if (minRefreshRate == mMinRefreshRate
+ && maxRefreshRate == mMaxRefreshRate
+ && defaultModeId == mDefaultModeId) {
+ return;
+ }
+ if (updateDesiredDisplayConfigSpecsInternalLocked(defaultModeId, minRefreshRate,
+ maxRefreshRate)) {
+ updateDeviceInfoLocked();
+ }
+ }
+
+ public boolean updateDesiredDisplayConfigSpecsInternalLocked(int defaultModeId,
+ float minRefreshRate, float maxRefreshRate) {
+ if (DEBUG) {
+ Slog.w(TAG, "updateDesiredDisplayConfigSpecsInternalLocked("
+ + "defaultModeId="
+ + Integer.toString(defaultModeId)
+ + ", minRefreshRate="
+ + Float.toString(minRefreshRate)
+ + ", maxRefreshRate="
+ + Float.toString(minRefreshRate));
+ }
+
+ final IBinder token = getDisplayTokenLocked();
+ SurfaceControl.setDesiredDisplayConfigSpecs(token, defaultModeId, minRefreshRate,
+ maxRefreshRate);
+ int activePhysIndex = SurfaceControl.getActiveConfig(token);
+ return updateActiveModeLocked(activePhysIndex);
+ }
+
public boolean updateAllowedModesInternalLocked(int[] allowedModes) {
if (DEBUG) {
Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes="
@@ -735,6 +772,8 @@
pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId);
pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes));
pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds));
+ pw.println("mMinRefreshRate=" + mMinRefreshRate);
+ pw.println("mMaxRefreshRate=" + mMaxRefreshRate);
pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid);
pw.println("mActivePhysIndex=" + mActivePhysIndex);
pw.println("mActiveModeId=" + mActiveModeId);
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index dcef998..f4b2dc8 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -352,11 +352,12 @@
// Set the color mode and allowed display mode.
if (device == mPrimaryDisplayDevice) {
- device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
+ // See ag/9588196 for correct values.
+ device.setDesiredDisplayConfigSpecs(0, 60, 60, mAllowedDisplayModes);
device.setRequestedColorModeLocked(mRequestedColorMode);
} else {
// Reset to default for non primary displays
- device.setAllowedDisplayModesLocked(new int[] {0});
+ device.setDesiredDisplayConfigSpecs(0, 60, 60, new int[] {0});
device.setRequestedColorModeLocked(0);
}
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 60cfbd0..739dd64 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -315,16 +315,9 @@
}
@Override
- public void setAllowedDisplayModesLocked(int[] modes) {
- final int id;
- if (modes.length > 0) {
- // The allowed modes should be ordered by preference, so just use the first mode
- // here.
- id = modes[0];
- } else {
- // If we don't have any allowed modes, just use the default mode.
- id = 0;
- }
+ public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate,
+ float maxRefreshRate, int[] modes) {
+ final int id = defaultModeId;
int index = -1;
if (id == 0) {
// Use the default.
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 283a78b..2992f1e 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -508,8 +508,7 @@
Slog.d(TAG, "updateDesiredDevice: new information "
+ describeWifiP2pDevice(device));
}
- mDesiredDevice.updateSupplicantDetails(device);
- mDesiredDevice.status = device.status;
+ mDesiredDevice.update(device);
if (mAdvertisedDisplay != null
&& mAdvertisedDisplay.getDeviceAddress().equals(address)) {
readvertiseDisplay(createWifiDisplay(mDesiredDevice));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9a85f952..a702ce5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1557,6 +1557,18 @@
if (!connected) {
removeCecSwitches(portId);
}
+
+ // Turning System Audio Mode off when the AVR is unlugged or standby.
+ // When the device is not unplugged but reawaken from standby, we check if the System
+ // Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly.
+ if (getAvrDeviceInfo() != null && portId == getAvrDeviceInfo().getPortId()) {
+ if (!connected) {
+ setSystemAudioMode(false);
+ } else if (mSystemAudioControlFeatureEnabled != mService.isSystemAudioActivated()){
+ setSystemAudioMode(mSystemAudioControlFeatureEnabled);
+ }
+ }
+
// Tv device will have permanent HotplugDetectionAction.
List<HotplugDetectionAction> hotplugActions = getActions(HotplugDetectionAction.class);
if (!hotplugActions.isEmpty()) {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 67a23dd..16b7d99 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -171,7 +171,6 @@
private final ArrayList<InputDevice>
mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
private boolean mKeyboardLayoutNotificationShown;
- private PendingIntent mKeyboardLayoutIntent;
private Toast mSwitchedKeyboardLayoutToast;
// State for vibrator tokens.
@@ -205,6 +204,7 @@
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
+ private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
int policyFlags);
@@ -603,6 +603,25 @@
}
}
+ /**
+ * Set the state of the touch mode.
+ *
+ * WindowManager remains the source of truth of the touch mode state.
+ * However, we need to keep a copy of this state in input.
+ *
+ * The apps determine the touch mode state. Therefore, a single app will
+ * affect the global state. That state change needs to be propagated to
+ * other apps, when they become focused.
+ *
+ * When input dispatches focus to the apps, the touch mode state
+ * will be sent together with the focus change.
+ *
+ * @param inTouchMode true if the device is in touch mode.
+ */
+ public void setInTouchMode(boolean inTouchMode) {
+ nativeSetInTouchMode(mPtr, inTouchMode);
+ }
+
@Override // Binder call
public boolean injectInputEvent(InputEvent event, int mode) {
return injectInputEventInternal(event, mode);
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 111b95a..aad177e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -25,7 +25,7 @@
public class RuleBinaryParser implements RuleParser {
@Override
- public List<Rule> parse(String ruleText) {
+ public List<Rule> parse(byte[] ruleBytes) {
// TODO: Implement binary text parser.
return null;
}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java
index 4e1f914..81783d5 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleParser.java
@@ -24,8 +24,8 @@
/** A helper class to parse rules into the {@link Rule} model. */
public interface RuleParser {
- /** Parse rules from a string. */
- List<Rule> parse(String ruleText) throws RuleParseException;
+ /** Parse rules from bytes. */
+ List<Rule> parse(byte[] ruleBytes) throws RuleParseException;
/** Parse rules from an input stream. */
List<Rule> parse(InputStream inputStream) throws RuleParseException;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
index 1212a08..2e99d0f 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -51,10 +51,10 @@
private static final String IS_HASHED_VALUE_ATTRIBUTE = "H";
@Override
- public List<Rule> parse(String ruleText) throws RuleParseException {
+ public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
- xmlPullParser.setInput(new StringReader(ruleText));
+ xmlPullParser.setInput(new StringReader(new String(ruleBytes, StandardCharsets.UTF_8)));
return parseRules(xmlPullParser);
} catch (Exception e) {
throw new RuleParseException(e.getMessage(), e);
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 ace15bd..7ecd624 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -20,17 +20,19 @@
import java.io.OutputStream;
import java.util.List;
+import java.util.Optional;
/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
public class RuleBinarySerializer implements RuleSerializer {
@Override
- public void serialize(List<Rule> rules, OutputStream outputStream) {
+ public void serialize(
+ List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream) {
// TODO: Implement stream serializer.
}
@Override
- public String serialize(List<Rule> rules) {
+ public String serialize(List<Rule> rules, Optional<Integer> formatVersion) {
// TODO: Implement text serializer.
return null;
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
index c45f6be..1125f78 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
@@ -20,13 +20,16 @@
import java.io.OutputStream;
import java.util.List;
+import java.util.Optional;
/** A helper class to serialize rules from the {@link Rule} model. */
public interface RuleSerializer {
/** Serialize rules to an output stream */
- void serialize(List<Rule> rules, OutputStream outputStream) throws RuleSerializeException;
+ void serialize(List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+ throws RuleSerializeException;
/** Serialize rules to a string. */
- String serialize(List<Rule> rule) throws RuleSerializeException;
+ String serialize(List<Rule> rule, Optional<Integer> formatVersion)
+ throws RuleSerializeException;
}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 5dd7891..254baba 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -29,6 +29,7 @@
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Optional;
/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
public class RuleXmlSerializer implements RuleSerializer {
@@ -48,7 +49,8 @@
private static final String IS_HASHED_VALUE_ATTRIBUTE = "H";
@Override
- public void serialize(List<Rule> rules, OutputStream outputStream)
+ public void serialize(
+ List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
throws RuleSerializeException {
try {
XmlSerializer xmlSerializer = Xml.newSerializer();
@@ -60,7 +62,8 @@
}
@Override
- public String serialize(List<Rule> rules) throws RuleSerializeException {
+ public String serialize(List<Rule> rules, Optional<Integer> formatVersion)
+ throws RuleSerializeException {
try {
XmlSerializer xmlSerializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
@@ -108,8 +111,8 @@
return;
}
xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG);
- serializeAttributeValue(CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()),
- xmlSerializer);
+ serializeAttributeValue(
+ CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
for (Formula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, xmlSerializer);
}
@@ -122,24 +125,30 @@
return;
}
xmlSerializer.startTag(NAMESPACE, ATOMIC_FORMULA_TAG);
- serializeAttributeValue(KEY_ATTRIBUTE, String.valueOf(atomicFormula.getKey()),
- xmlSerializer);
+ serializeAttributeValue(
+ KEY_ATTRIBUTE, String.valueOf(atomicFormula.getKey()), xmlSerializer);
if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) {
- serializeAttributeValue(VALUE_ATTRIBUTE,
- ((AtomicFormula.StringAtomicFormula) atomicFormula).getValue(), xmlSerializer);
- serializeAttributeValue(IS_HASHED_VALUE_ATTRIBUTE,
+ serializeAttributeValue(
+ VALUE_ATTRIBUTE,
+ ((AtomicFormula.StringAtomicFormula) atomicFormula).getValue(),
+ xmlSerializer);
+ serializeAttributeValue(
+ IS_HASHED_VALUE_ATTRIBUTE,
String.valueOf(
((AtomicFormula.StringAtomicFormula) atomicFormula).getIsHashedValue()),
xmlSerializer);
} else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) {
- serializeAttributeValue(OPERATOR_ATTRIBUTE,
+ serializeAttributeValue(
+ OPERATOR_ATTRIBUTE,
String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getOperator()),
xmlSerializer);
- serializeAttributeValue(VALUE_ATTRIBUTE,
+ serializeAttributeValue(
+ VALUE_ATTRIBUTE,
String.valueOf(((AtomicFormula.IntAtomicFormula) atomicFormula).getValue()),
xmlSerializer);
} else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) {
- serializeAttributeValue(VALUE_ATTRIBUTE,
+ serializeAttributeValue(
+ VALUE_ATTRIBUTE,
String.valueOf(((AtomicFormula.BooleanAtomicFormula) atomicFormula).getValue()),
xmlSerializer);
} else {
@@ -149,9 +158,8 @@
xmlSerializer.endTag(NAMESPACE, ATOMIC_FORMULA_TAG);
}
- private void serializeAttributeValue(String attribute, String value,
- XmlSerializer xmlSerializer)
- throws IOException {
+ private void serializeAttributeValue(
+ String attribute, String value, XmlSerializer xmlSerializer) throws IOException {
if (value == null) {
return;
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index c84745e..557ba54 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1447,11 +1447,13 @@
private float[] mSvElevations;
private float[] mSvAzimuths;
private float[] mSvCarrierFreqs;
+ private float[] mBasebandCn0s;
}
@NativeEntryPoint
private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
- float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
+ float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs,
+ float[] basebandCn0s) {
SvStatusInfo svStatusInfo = new SvStatusInfo();
svStatusInfo.mSvCount = svCount;
svStatusInfo.mSvidWithFlags = svidWithFlags;
@@ -1459,6 +1461,7 @@
svStatusInfo.mSvElevations = svElevations;
svStatusInfo.mSvAzimuths = svAzimuths;
svStatusInfo.mSvCarrierFreqs = svCarrierFreqs;
+ svStatusInfo.mBasebandCn0s = basebandCn0s;
sendMessage(REPORT_SV_STATUS, 0, svStatusInfo);
}
@@ -1470,7 +1473,8 @@
info.mCn0s,
info.mSvElevations,
info.mSvAzimuths,
- info.mSvCarrierFreqs);
+ info.mSvCarrierFreqs,
+ info.mBasebandCn0s);
// Log CN0 as part of GNSS metrics
mGnssMetrics.logCn0(info.mCn0s, info.mSvCount, info.mSvCarrierFreqs);
@@ -1485,7 +1489,8 @@
info.mCn0s,
info.mSvElevations,
info.mSvAzimuths,
- info.mSvCarrierFreqs);
+ info.mSvCarrierFreqs,
+ info.mBasebandCn0s);
int usedInFixCount = 0;
int maxCn0 = 0;
int meanCn0 = 0;
@@ -1501,13 +1506,15 @@
if (VERBOSE) {
Log.v(TAG, "svid: " + gnssStatus.getSvid(i)
+ " cn0: " + gnssStatus.getCn0DbHz(i)
+ + " basebandCn0: " + gnssStatus.getBasebandCn0DbHz(i)
+ " elev: " + gnssStatus.getElevationDegrees(i)
+ " azimuth: " + gnssStatus.getAzimuthDegrees(i)
+ " carrier frequency: " + gnssStatus.getCn0DbHz(i)
+ (gnssStatus.hasEphemerisData(i) ? " E" : " ")
+ (gnssStatus.hasAlmanacData(i) ? " A" : " ")
+ (gnssStatus.usedInFix(i) ? "U" : "")
- + (gnssStatus.hasCarrierFrequencyHz(i) ? "F" : ""));
+ + (gnssStatus.hasCarrierFrequencyHz(i) ? "F" : "")
+ + (gnssStatus.hasBasebandCn0DbHz(i) ? "B" : ""));
}
}
if (usedInFixCount > 0) {
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index d67d0c5..eaf63c8 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -71,7 +71,8 @@
final float[] cn0s,
final float[] elevations,
final float[] azimuths,
- final float[] carrierFreqs) {
+ final float[] carrierFreqs,
+ final float[] basebandCn0s) {
foreach((IGnssStatusListener listener, CallerIdentity callerIdentity) -> {
if (!hasPermission(mContext, callerIdentity)) {
logPermissionDisabledEventNotReported(TAG, callerIdentity.mPackageName,
@@ -79,7 +80,7 @@
return;
}
listener.onSvStatusChanged(svCount, prnWithFlags, cn0s, elevations, azimuths,
- carrierFreqs);
+ carrierFreqs, basebandCn0s);
});
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a4e7ac4..83b6215 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -181,6 +181,7 @@
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
+ private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
// No challenge provided
private static final int CHALLENGE_NONE = 0;
@@ -661,6 +662,34 @@
}
/**
+ * Clean up states associated with the given user, in case the userId is reused but LSS didn't
+ * get a chance to do cleanup previously during ACTION_USER_REMOVED.
+ *
+ * Internally, LSS stores serial number for each user and check it against the current user's
+ * serial number to determine if the userId is reused and invoke cleanup code.
+ */
+ private void cleanupDataForReusedUserIdIfNecessary(int userId) {
+ if (userId == UserHandle.USER_SYSTEM) {
+ // Short circuit as we never clean up user 0.
+ return;
+ }
+ // Serial number is never reusued, so we can use it as a distinguisher for user Id reuse.
+ int serialNumber = mUserManager.getUserSerialNumber(userId);
+
+ int storedSerialNumber = getIntUnchecked(USER_SERIAL_NUMBER_KEY, -1, userId);
+ if (storedSerialNumber != serialNumber) {
+ // If LockSettingsStorage does not have a copy of the serial number, it could be either
+ // this is a user created before the serial number recording logic is introduced, or
+ // the user does not exist or was removed and cleaned up properly. In either case, don't
+ // invoke removeUser().
+ if (storedSerialNumber != -1) {
+ removeUser(userId, /* unknownUser */ true);
+ }
+ setIntUnchecked(USER_SERIAL_NUMBER_KEY, serialNumber, userId);
+ }
+ }
+
+ /**
* Check if profile got unlocked but the keystore is still locked. This happens on full disk
* encryption devices since the profile may not yet be running when we consider unlocking it
* during the normal flow. In this case unlock the keystore for the profile.
@@ -684,6 +713,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
+ cleanupDataForReusedUserIdIfNecessary(userId);
ensureProfileKeystoreUnlocked(userId);
// Hide notification first, as tie managed profile lock takes time
hideEncryptionNotification(new UserHandle(userId));
@@ -729,9 +759,6 @@
if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
// Notify keystore that a new user was added.
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- if (userHandle > UserHandle.USER_SYSTEM) {
- removeUser(userHandle, /* unknownUser= */ true);
- }
final KeyStore ks = KeyStore.getInstance();
final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
final int parentHandle = parentInfo != null ? parentInfo.id : -1;
@@ -1066,6 +1093,10 @@
setStringUnchecked(key, userId, Long.toString(value));
}
+ private void setIntUnchecked(String key, int value, int userId) {
+ setStringUnchecked(key, userId, Integer.toString(value));
+ }
+
@Override
public void setString(String key, String value, int userId) {
checkWritePermission(userId);
@@ -1104,6 +1135,11 @@
return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value);
}
+ private int getIntUnchecked(String key, int defaultValue, int userId) {
+ String value = getStringUnchecked(key, null, userId);
+ return TextUtils.isEmpty(value) ? defaultValue : Integer.parseInt(value);
+ }
+
@Override
public String getString(String key, String defaultValue, int userId) {
checkReadPermission(key, userId);
@@ -2171,8 +2207,8 @@
}
private void removeUser(int userId, boolean unknownUser) {
+ Slog.i(TAG, "RemoveUser: " + userId);
mSpManager.removeUser(userId);
- mStorage.removeUser(userId);
mStrongAuth.removeUser(userId);
tryRemoveUserFromSpCacheLater(userId);
@@ -2183,6 +2219,9 @@
if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
removeKeystoreProfileKey(userId);
}
+ // Clean up storage last, this is to ensure that cleanupDataForReusedUserIdIfNecessary()
+ // can make the assumption that no USER_SERIAL_NUMBER_KEY means user is fully removed.
+ mStorage.removeUser(userId);
}
private void removeKeystoreProfileKey(int targetUserId) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 91c9253..9a49c16 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.Intent;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
+import android.os.Bundle;
import java.util.Objects;
@@ -29,7 +30,7 @@
final ComponentName mComponentName;
final String mUniqueId;
- private Callback mCallback;
+ Callback mCallback;
private MediaRoute2ProviderInfo mProviderInfo;
MediaRoute2Provider(@NonNull ComponentName componentName) {
@@ -77,6 +78,9 @@
}
public interface Callback {
- void onProviderStateChanged(MediaRoute2Provider provider);
+ void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
+ void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
+ @NonNull String clientPackageName, @NonNull MediaRoute2Info route,
+ @Nullable Bundle controlHints, int seq);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 3b6580a..a5abb18 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.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
@@ -253,6 +254,20 @@
setAndNotifyProviderInfo(info);
}
+ private void onRouteSelected(Connection connection,
+ String packageName, String routeId, Bundle controlHints, int seq) {
+ if (mActiveConnection != connection) {
+ return;
+ }
+ MediaRoute2ProviderInfo providerInfo = getProviderInfo();
+ MediaRoute2Info route = (providerInfo == null) ? null : providerInfo.getRoute(routeId);
+ if (route == null) {
+ Slog.w(TAG, this + ": Unknown route " + routeId + " is selected from remove provider");
+ return;
+ }
+ mCallback.onRouteSelected(this, packageName, route, controlHints, seq);
+ }
+
private void disconnect() {
if (mActiveConnection != null) {
mConnectionReady = false;
@@ -341,6 +356,11 @@
void postProviderInfoUpdated(MediaRoute2ProviderInfo info) {
mHandler.post(() -> onProviderInfoUpdated(Connection.this, info));
}
+
+ void postRouteSelected(String packageName, String routeId, Bundle controlHints, int seq) {
+ mHandler.post(() -> onRouteSelected(Connection.this,
+ packageName, routeId, controlHints, seq));
+ }
}
private static final class ProviderClient extends IMediaRoute2ProviderClient.Stub {
@@ -361,5 +381,15 @@
connection.postProviderInfoUpdated(info);
}
}
+
+ @Override
+ public void notifyRouteSelected(String packageName, String routeId,
+ Bundle controlHints, int seq) {
+ Connection connection = mConnectionRef.get();
+ if (connection != null) {
+ connection.postRouteSelected(packageName, routeId, controlHints, seq);
+ }
+ }
+
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 2cf920d..2c478df 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -77,7 +77,7 @@
@GuardedBy("mLock")
private int mCurrentUserId = -1;
@GuardedBy("mLock")
- private int mSelectRouteRequestSequenceNumber = 0;
+ private int mSelectRouteRequestSequenceNumber = 1;
MediaRouter2ServiceImpl(Context context) {
mContext = context;
@@ -218,7 +218,7 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), route);
+ requestSelectRoute2Locked(mAllClientRecords.get(client.asBinder()), false, route);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -399,10 +399,12 @@
}
}
- private void requestSelectRoute2Locked(ClientRecord clientRecord, MediaRoute2Info route) {
+ private void requestSelectRoute2Locked(ClientRecord clientRecord, boolean selectedByManager,
+ MediaRoute2Info route) {
if (clientRecord != null) {
MediaRoute2Info oldRoute = clientRecord.mSelectedRoute;
clientRecord.mSelectingRoute = route;
+ clientRecord.mIsManagerSelecting = selectedByManager;
UserHandler handler = clientRecord.mUserRecord.mHandler;
//TODO: Handle transfer instead of unselect and select
@@ -417,7 +419,6 @@
handler.sendMessage(obtainMessage(
UserHandler::requestSelectRoute, handler, clientRecord.mPackageName,
route, seq));
-
// Remove all previous timeout messages
for (int previousSeq : clientRecord.mSelectRouteSequenceNumbers) {
clientRecord.mUserRecord.mHandler.removeMessages(previousSeq);
@@ -543,7 +544,7 @@
Slog.w(TAG, "Ignoring route selection for unknown client.");
}
if (clientRecord != null && managerRecord.mTrusted) {
- requestSelectRoute2Locked(clientRecord, route);
+ requestSelectRoute2Locked(clientRecord, true, route);
}
}
}
@@ -656,7 +657,9 @@
public final UserRecord mUserRecord;
public final String mPackageName;
public final List<Integer> mSelectRouteSequenceNumbers;
+
public List<String> mControlCategories;
+ public boolean mIsManagerSelecting;
public MediaRoute2Info mSelectingRoute;
public MediaRoute2Info mSelectedRoute;
@@ -802,9 +805,8 @@
sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
}
- // TODO: When introducing MediaRoute2ProviderService#sendControlHints(),
- // Make this method to be called.
- public void onRouteSelectionRequestHandled(@NonNull MediaRoute2ProviderProxy provider,
+ @Override
+ public void onRouteSelected(@NonNull MediaRoute2ProviderProxy provider,
String clientPackageName, MediaRoute2Info route, Bundle controlHints, int seq) {
sendMessage(PooledLambda.obtainMessage(
UserHandler::updateSelectedRoute, this, provider, clientPackageName, route,
@@ -917,6 +919,8 @@
return;
}
+ //TODO: handle a case such that controlHints is null. (How should we notify MR2?)
+
if (clientRecord.mSelectingRoute == null || !TextUtils.equals(
clientRecord.mSelectingRoute.getUniqueId(), selectedRoute.getUniqueId())) {
Log.w(TAG, "Ignoring invalid updateSelectedRoute call. selectingRoute="
@@ -929,7 +933,9 @@
notifyRouteSelectedToClient(((Client2Record) clientRecord).mClient,
selectedRoute,
- MediaRouter2.SELECT_REASON_USER_SELECTED,
+ clientRecord.mIsManagerSelecting
+ ? MediaRouter2.SELECT_REASON_SYSTEM_SELECTED :
+ MediaRouter2.SELECT_REASON_USER_SELECTED,
controlHints);
updateClientUsage(clientRecord);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 08c9426..e473c96 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -138,7 +138,6 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
-import com.android.server.connectivity.Tethering;
import java.io.File;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 863991c..3c76c67 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -8268,7 +8268,7 @@
try {
assistant.onAllowedAdjustmentsChanged();
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify assistant (capabilities): " + assistant, ex);
+ Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
}
}
@@ -8278,7 +8278,7 @@
try {
assistant.onNotificationsSeen(keys);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify assistant (seen): " + assistant, ex);
+ Slog.e(TAG, "unable to notify assistant (seen): " + info, ex);
}
}
@@ -8584,7 +8584,7 @@
listener.onStatusBarIconsBehaviorChanged(hideSilentStatusIcons);
} catch (RemoteException ex) {
Slog.e(TAG, "unable to notify listener "
- + "(hideSilentStatusIcons): " + listener, ex);
+ + "(hideSilentStatusIcons): " + info, ex);
}
});
}
@@ -8877,7 +8877,7 @@
try {
listener.onNotificationPosted(sbnHolder, rankingUpdate);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (posted): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (posted): " + info, ex);
}
}
@@ -8891,7 +8891,7 @@
try {
listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (removed): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (removed): " + info, ex);
}
}
@@ -8901,7 +8901,7 @@
try {
listener.onNotificationRankingUpdate(rankingUpdate);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (ranking update): " + info, ex);
}
}
@@ -8910,7 +8910,7 @@
try {
listener.onListenerHintsChanged(hints);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (listener hints): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (listener hints): " + info, ex);
}
}
@@ -8920,7 +8920,7 @@
try {
listener.onInterruptionFilterChanged(interruptionFilter);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (interruption filter): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (interruption filter): " + info, ex);
}
}
@@ -8931,7 +8931,7 @@
try {
listener.onNotificationChannelModification(pkg, user, channel, modificationType);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (channel changed): " + info, ex);
}
}
@@ -8942,7 +8942,7 @@
try {
listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
} catch (RemoteException ex) {
- Slog.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
+ Slog.e(TAG, "unable to notify listener (channel group changed): " + info, ex);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 259200b..7e47800 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -19,6 +19,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
@@ -309,6 +310,7 @@
final String nativeLibraryRootStr = initialLibraryPaths.nativeLibraryRootDir;
final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa;
+ final boolean onIncremental = isIncrementalPath(pkg.codePath);
String primaryCpuAbi = null;
String secondaryCpuAbi = null;
@@ -341,10 +343,18 @@
int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
- useIsaSpecificSubdirs);
+ if (onIncremental) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
+ "incrementalNativeBinaries");
+ abi32 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
+ handle, nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ }
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi32 = NativeLibraryHelper.findSupportedAbi(
@@ -364,10 +374,18 @@
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
- useIsaSpecificSubdirs);
+ if (onIncremental) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
+ "incrementalNativeBinaries");
+ abi64 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
+ handle, nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+ useIsaSpecificSubdirs);
+ }
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
abi64 = NativeLibraryHelper.findSupportedAbi(
@@ -418,9 +436,15 @@
final int copyRet;
if (extractLibs) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
- copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
- nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ if (onIncremental) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "incrementalNativeBinaries");
+ copyRet = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
+ handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ } else {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+ copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+ nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
+ }
} else {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0e075b1..4e0e4ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -87,6 +87,7 @@
import static android.content.pm.PackageManager.RESTRICTION_NONE;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
@@ -226,6 +227,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.incremental.IncrementalManager;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
import android.os.storage.StorageEventListener;
@@ -1074,6 +1076,8 @@
private Future<?> mPrepareAppDataFuture;
+ private final IncrementalManager mIncrementalManager;
+
private static class IFVerificationParams {
PackageParser.Package pkg;
boolean replacing;
@@ -2360,6 +2364,37 @@
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
t.traceEnd(); // "create package manager"
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ packageName -> {
+ synchronized (m.mInstallLock) {
+ final PackageParser.Package pkg;
+ final SharedUserSetting sharedUser;
+ synchronized (m.mLock) {
+ PackageSetting ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
+ }
+ pkg = ps.pkg;
+ sharedUser = ps.sharedUser;
+ }
+
+ if (pkg == null) {
+ Slog.e(TAG, "Failed to find package " + packageName);
+ return;
+ }
+ final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+ m.mInjector.getCompatibility());
+
+ if (!newSeInfo.equals(pkg.applicationInfo.seInfo)) {
+ Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ + pkg.applicationInfo.seInfo + " to: " + newSeInfo);
+ pkg.applicationInfo.seInfo = newSeInfo;
+ m.prepareAppDataAfterInstallLIF(pkg);
+ }
+ }
+ });
+
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
@@ -2509,6 +2544,8 @@
mPermissionManager = injector.getPermissionManagerServiceInternal();
mSettings = injector.getSettings();
mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr");
+ mIncrementalManager =
+ (IncrementalManager) mContext.getSystemService(Context.INCREMENTAL_SERVICE);
// CHECKSTYLE:ON IndentationCheck
t.traceEnd();
@@ -8798,6 +8835,7 @@
// Full APK verification can be skipped during certificate collection, only if the file is
// in verified partition, or can be verified on access (when apk verity is enabled). In both
// cases, only data in Signing Block is verified instead of the whole file.
+ // TODO(b/136132412): skip for Incremental installation
final boolean skipVerify = scanSystemPartition
|| (forceCollect && canSkipForcedPackageVerification(pkg));
collectCertificatesLI(pkgSetting, pkg, forceCollect, skipVerify);
@@ -10777,24 +10815,8 @@
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
- // Apps which share a sharedUserId must be placed in the same selinux domain. If this
- // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
- // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
- // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
- // least restrictive selinux domain.
- // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
- // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
- // ensures that all packages continue to run in the same selinux domain.
- final int targetSdkVersion =
- ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) ?
- sharedUserSetting.seInfoTargetSdkVersion : pkg.applicationInfo.targetSdkVersion;
- // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
- // They currently can be if the sharedUser apps are signed with the platform key.
- final boolean isPrivileged = (sharedUserSetting != null) ?
- sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
-
- pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, isPrivileged,
- targetSdkVersion);
+ pkg.applicationInfo.seInfo = SELinuxMMAC.getSeInfo(pkg, sharedUserSetting,
+ injector.getCompatibility());
pkg.applicationInfo.seInfoUser = SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(
userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId));
@@ -14675,9 +14697,16 @@
final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);
if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);
+ final boolean onIncremental = mIncrementalManager != null
+ && isIncrementalPath(beforeCodeFile.getAbsolutePath());
try {
- Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
- } catch (ErrnoException e) {
+ if (onIncremental) {
+ mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(),
+ afterCodeFile.getAbsolutePath());
+ } else {
+ Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());
+ }
+ } catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to rename", e);
return false;
}
@@ -14737,6 +14766,11 @@
return false;
}
+ String codePath = codeFile.getAbsolutePath();
+ if (mIncrementalManager != null && isIncrementalPath(codePath)) {
+ mIncrementalManager.closeStorage(codePath);
+ }
+
removeCodePathLI(codeFile);
if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
@@ -15928,6 +15962,8 @@
& PackageManagerService.SCAN_AS_INSTANT_APP) != 0);
final PackageParser.Package pkg = reconciledPkg.pkgSetting.pkg;
final String packageName = pkg.packageName;
+ final boolean onIncremental = mIncrementalManager != null
+ && isIncrementalPath(pkg.codePath);
prepareAppDataAfterInstallLIF(pkg);
if (reconciledPkg.prepareResult.clearCodeCache) {
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
@@ -15958,6 +15994,7 @@
// We only need to dexopt if the package meets ALL of the following conditions:
// 1) it is not an instant app or if it is then dexopt is enabled via gservices.
// 2) it is not debuggable.
+ // 3) it is not on Incremental File System.
//
// Note that we do not dexopt instant apps by default. dexopt can take some time to
// complete, so we skip this step during installation. Instead, we'll take extra time
@@ -15968,7 +16005,8 @@
final boolean performDexopt =
(!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
- && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
+ && ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0)
+ && (!onIncremental);
if (performDexopt) {
// Compile the layout resources.
@@ -16200,6 +16238,7 @@
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
pkg.setSigningDetails(args.signingDetails);
} else {
+ // TODO(b/136132412): skip for Incremental installation
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
@@ -23523,7 +23562,7 @@
@Override
public void uninstallApex(String packageName, long versionCode, int userId,
- IntentSender intentSender) {
+ IntentSender intentSender, int flags) {
final int callerUid = Binder.getCallingUid();
if (callerUid != Process.ROOT_UID && callerUid != Process.SHELL_UID) {
throw new SecurityException("Not allowed to uninstall apexes");
@@ -23532,7 +23571,7 @@
new PackageInstallerService.PackageDeleteObserverAdapter(
PackageManagerService.this.mContext, intentSender, packageName,
false, userId);
- if (userId != UserHandle.USER_ALL) {
+ if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
"Can't uninstall an apex for a single user");
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6d86078e..1b73602 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1853,7 +1853,7 @@
if (internal.isApexPackage(packageName)) {
internal.uninstallApex(
- packageName, versionCode, translatedUserId, receiver.getIntentSender());
+ packageName, versionCode, translatedUserId, receiver.getIntentSender(), flags);
} else if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
final PackageInfo info = mInterface.getPackageInfo(packageName,
PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
@@ -3279,7 +3279,7 @@
pw.println(" [--user USER_ID] INTENT");
pw.println(" Prints all broadcast receivers that can handle the given INTENT.");
pw.println("");
- pw.println(" install [-rtsfdgw] [-i PACKAGE] [--user USER_ID|all|current]");
+ pw.println(" install [-rtfdgw] [-i PACKAGE] [--user USER_ID|all|current]");
pw.println(" [-p INHERIT_PACKAGE] [--install-location 0/1/2]");
pw.println(" [--install-reason 0/1/2/3/4] [--originating-uri URI]");
pw.println(" [--referrer URI] [--abi ABI_NAME] [--force-sdk]");
@@ -3293,7 +3293,6 @@
pw.println(" -R: disallow replacement of existing application");
pw.println(" -t: allow test packages");
pw.println(" -i: specify package name of installer owning the app");
- pw.println(" -s: install application on sdcard");
pw.println(" -f: install application on internal flash");
pw.println(" -d: allow version code downgrade (debuggable packages only)");
pw.println(" -p: partial application install (new split on top of existing pkg)");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b464988..d20f20f 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -16,6 +16,8 @@
package com.android.server.pm;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
@@ -23,6 +25,8 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.server.compat.PlatformCompat;
+
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -72,6 +76,19 @@
// Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
+ /**
+ * This change gates apps access to untrusted_app_R-targetSDk SELinux domain. Allows opt-in
+ * to R targetSdkVersion enforced changes without changing target SDK. Turning this change
+ * off for an app targeting R is a no-op.
+ *
+ * <p>Has no effect for apps using shared user id.
+ *
+ * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
+ */
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
+ @ChangeId
+ static final long SELINUX_LATEST_CHANGES = 143539591L;
+
// Only initialize sMacPermissions once.
static {
// Platform mac permissions.
@@ -319,6 +336,48 @@
}
}
+ private static int getTargetSdkVersionForSeInfo(PackageParser.Package pkg,
+ SharedUserSetting sharedUserSetting, PlatformCompat compatibility) {
+ // Apps which share a sharedUserId must be placed in the same selinux domain. If this
+ // package is the first app installed as this shared user, set seInfoTargetSdkVersion to its
+ // targetSdkVersion. These are later adjusted in PackageManagerService's constructor to be
+ // the lowest targetSdkVersion of all apps within the shared user, which corresponds to the
+ // least restrictive selinux domain.
+ // NOTE: As new packages are installed / updated, the shared user's seinfoTargetSdkVersion
+ // will NOT be modified until next boot, even if a lower targetSdkVersion is used. This
+ // ensures that all packages continue to run in the same selinux domain.
+ if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
+ return sharedUserSetting.seInfoTargetSdkVersion;
+ }
+ if (compatibility.isChangeEnabled(SELINUX_LATEST_CHANGES, pkg.applicationInfo)) {
+ return android.os.Build.VERSION_CODES.R;
+ }
+
+ return pkg.applicationInfo.targetSdkVersion;
+ }
+
+ /**
+ * Selects a security label to a package based on input parameters and the seinfo tag taken
+ * from a matched policy. All signature based policy stanzas are consulted and, if no match
+ * is found, the default seinfo label of 'default' is used. The security label is attached to
+ * the ApplicationInfo instance of the package.
+ *
+ * @param pkg object representing the package to be labeled.
+ * @param sharedUserSetting if the app shares a sharedUserId, then this has the shared setting.
+ * @param compatibility the PlatformCompat service to ask about state of compat changes.
+ * @return String representing the resulting seinfo.
+ */
+ public static String getSeInfo(PackageParser.Package pkg, SharedUserSetting sharedUserSetting,
+ PlatformCompat compatibility) {
+ final int targetSdkVersion = getTargetSdkVersionForSeInfo(pkg, sharedUserSetting,
+ compatibility);
+ // TODO(b/71593002): isPrivileged for sharedUser and appInfo should never be out of sync.
+ // They currently can be if the sharedUser apps are signed with the platform key.
+ final boolean isPrivileged = (sharedUserSetting != null)
+ ? sharedUserSetting.isPrivileged() | pkg.isPrivileged() : pkg.isPrivileged();
+ return getSeInfo(pkg, isPrivileged, targetSdkVersion);
+ }
+
/**
* Selects a security label to a package based on input parameters and the seinfo tag taken
* from a matched policy. All signature based policy stanzas are consulted and, if no match
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 27eefc6..7ea4e98 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -620,32 +620,6 @@
PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
"Cannot stage multiple sessions without checkpoint support", null);
}
-
- // TODO:b/141843321 Add support for staging multiple sessions in apexd
- // Since apexd doesn't support multiple staged sessions yet, we have to careful how
- // we handle apex sessions. We want to allow a set of apex sessions under the same
- // parent to be staged when there is no previously staged apex sessions.
- if (isApexSession(session) && isApexSession(stagedSession)) {
- // session is apex and it can co-exist with stagedSession only if they are from
- // same parent
- final boolean coExist;
- if (!session.hasParentSessionId() && !stagedSession.hasParentSessionId()) {
- // Both single package apex sessions. Cannot co-exist.
- coExist = false;
- } else {
- // At least one of the session has parent. Both must be from same parent.
- coExist =
- session.getParentSessionId() == stagedSession.getParentSessionId();
- }
- if (!coExist) {
- throw new PackageManagerException(
- PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
- "Package: " + session.getPackageName() + " in session: "
- + session.sessionId + " cannot be staged as there is "
- + "already another apex staged session: "
- + stagedSession.sessionId, null);
- }
- }
}
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8144338..05f62b2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1634,13 +1634,14 @@
* See {@link UserManagerInternal#setDevicePolicyUserRestrictions}
*/
private void setDevicePolicyUserRestrictionsInner(@UserIdInt int userId,
- @Nullable Bundle restrictions, boolean isDeviceOwner, int cameraRestrictionScope) {
+ @Nullable Bundle restrictions,
+ @UserManagerInternal.OwnerType int restrictionOwnerType) {
final Bundle global = new Bundle();
final Bundle local = new Bundle();
// Sort restrictions into local and global ensuring they don't overlap.
- UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, isDeviceOwner,
- cameraRestrictionScope, global, local);
+ UserRestrictionsUtils.sortToGlobalAndLocal(restrictions, restrictionOwnerType, global,
+ local);
boolean globalChanged, localChanged;
synchronized (mRestrictionsLock) {
@@ -1650,7 +1651,7 @@
localChanged = updateRestrictionsIfNeededLR(
userId, local, mDevicePolicyLocalUserRestrictions);
- if (isDeviceOwner) {
+ if (restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER) {
// Remember the global restriction owner userId to be able to make a distinction
// in getUserRestrictionSource on who set local policies.
mDeviceOwnerUserId = userId;
@@ -3209,6 +3210,13 @@
flags |= UserInfo.FLAG_EPHEMERAL;
}
+ // Always clear EPHEMERAL for pre-created users, otherwise the storage key
+ // won't be persisted. The flag will be re-added (if needed) when the
+ // pre-created user is "converted" to a normal user.
+ if (preCreate) {
+ flags &= ~UserInfo.FLAG_EPHEMERAL;
+ }
+
userInfo = new UserInfo(userId, name, null, flags, userType);
userInfo.serialNumber = mNextSerialNumber++;
userInfo.creationTime = getCreationTime();
@@ -4427,6 +4435,7 @@
pw.println(" Supports switchable users: " + UserManager.supportsMultipleUsers());
pw.println(" All guests ephemeral: " + Resources.getSystem().getBoolean(
com.android.internal.R.bool.config_guestUserEphemeral));
+ pw.println(" Force ephemeral users: " + mForceEphemeralUsers);
pw.println(" Is split-system user: " + UserManager.isSplitSystemUser());
pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
pw.println(" User version: " + mUserVersion);
@@ -4484,9 +4493,9 @@
private class LocalService extends UserManagerInternal {
@Override
public void setDevicePolicyUserRestrictions(@UserIdInt int userId,
- @Nullable Bundle restrictions, boolean isDeviceOwner, int cameraRestrictionScope) {
- UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId, restrictions,
- isDeviceOwner, cameraRestrictionScope);
+ @Nullable Bundle restrictions, @OwnerType int restrictionOwnerType) {
+ UserManagerService.this.setDevicePolicyUserRestrictionsInner(userId,
+ restrictions, restrictionOwnerType);
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3be51c5..9b50307 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -194,7 +194,18 @@
UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_RUN_IN_BACKGROUND,
UserManager.DISALLOW_UNMUTE_MICROPHONE,
- UserManager.DISALLOW_UNMUTE_DEVICE
+ UserManager.DISALLOW_UNMUTE_DEVICE,
+ UserManager.DISALLOW_CAMERA
+ );
+
+ /**
+ * Special user restrictions that are applied globally when set by the profile owner of a
+ * managed profile that was created during the device provisioning flow.
+ */
+ private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
+ Sets.newArraySet(
+ UserManager.DISALLOW_CONFIG_DATE_TIME,
+ UserManager.DISALLOW_CAMERA
);
/**
@@ -419,15 +430,9 @@
* Takes restrictions that can be set by device owner, and sort them into what should be applied
* globally and what should be applied only on the current user.
*/
- public static void sortToGlobalAndLocal(@Nullable Bundle in, boolean isDeviceOwner,
- int cameraRestrictionScope,
- @NonNull Bundle global, @NonNull Bundle local) {
- // Camera restriction (as well as all others) goes to at most one bundle.
- if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_GLOBALLY) {
- global.putBoolean(UserManager.DISALLOW_CAMERA, true);
- } else if (cameraRestrictionScope == UserManagerInternal.CAMERA_DISABLED_LOCALLY) {
- local.putBoolean(UserManager.DISALLOW_CAMERA, true);
- }
+ public static void sortToGlobalAndLocal(@Nullable Bundle in,
+ @UserManagerInternal.OwnerType int restrictionOwnerType, @NonNull Bundle global,
+ @NonNull Bundle local) {
if (in == null || in.size() == 0) {
return;
}
@@ -435,7 +440,7 @@
if (!in.getBoolean(key)) {
continue;
}
- if (isGlobal(isDeviceOwner, key)) {
+ if (isGlobal(restrictionOwnerType, key)) {
global.putBoolean(key, true);
} else {
local.putBoolean(key, true);
@@ -446,9 +451,13 @@
/**
* Whether given user restriction should be enforced globally.
*/
- private static boolean isGlobal(boolean isDeviceOwner, String key) {
- return (isDeviceOwner &&
- (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+ private static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
+ String key) {
+ return ((restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER) && (
+ PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+ || ((restrictionOwnerType
+ == UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)
+ && PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS.contains(key))
|| PROFILE_GLOBAL_RESTRICTIONS.contains(key)
|| DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key);
}
@@ -569,10 +578,6 @@
if (newValue) {
android.provider.Settings.Global.putStringForUser(
context.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
- userId);
- android.provider.Settings.Global.putStringForUser(
- context.getContentResolver(),
android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
userId);
}
@@ -708,7 +713,6 @@
restriction = UserManager.DISALLOW_DEBUGGING_FEATURES;
break;
- case android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE:
case android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB:
if ("1".equals(value)) {
return false;
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 6abfbdc..0a6a435 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -85,28 +85,30 @@
* System Property whether to only install system packages on a user if they're whitelisted for
* that user type. These are flags and can be freely combined.
* <ul>
- * <li> 0 (0b000) - disable whitelist (install all system packages; no logging)</li>
- * <li> 1 (0b001) - enforce (only install system packages if they are whitelisted)</li>
- * <li> 2 (0b010) - log (log when a non-whitelisted package is run)</li>
- * <li> 4 (0b100) - implicitly whitelist any package not mentioned in the whitelist</li>
- * <li>-1 - use device default (as defined in res/res/values/config.xml)</li>
+ * <li> 0 (0b0000) - disable whitelist (install all system packages; no logging)</li>
+ * <li> 1 (0b0001) - enforce (only install system packages if they are whitelisted)</li>
+ * <li> 2 (0b0010) - log (log when a non-whitelisted package is run)</li>
+ * <li> 4 (0b0100) - implicitly whitelist any package not mentioned in the whitelist</li>
+ * <li> 8 (0b1000) - ignore OTAs (don't install system packages during OTAs)</li>
+ * <li>-1 - use device default (as defined in res/res/values/config.xml)</li>
* </ul>
* Note: This list must be kept current with config_userTypePackageWhitelistMode in
* frameworks/base/core/res/res/values/config.xml
*/
static final String PACKAGE_WHITELIST_MODE_PROP = "persist.debug.user.package_whitelist_mode";
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE = 0;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b001;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b010;
- static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b100;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE = 0b0001;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_LOG = 0b0010;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST = 0b0100;
+ static final int USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA = 0b1000;
static final int USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT = -1;
@IntDef(flag = true, prefix = "USER_TYPE_PACKAGE_WHITELIST_MODE_", value = {
USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE,
USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
- USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE,
USER_TYPE_PACKAGE_WHITELIST_MODE_LOG,
USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST,
+ USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PackageWhitelistMode {}
@@ -169,11 +171,17 @@
boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
final int mode = getWhitelistMode();
checkWhitelistedSystemPackages(mode);
- if (!isUpgrade && !isFirstBoot) {
+ final boolean isConsideredUpgrade = isUpgrade && !isIgnoreOtaMode(mode);
+ if (!isConsideredUpgrade && !isFirstBoot) {
+ return false;
+ }
+ if (isFirstBoot && !isEnforceMode(mode)) {
+ // Note that if !isEnforceMode, we nonetheless still install packages if isUpgrade
+ // in order to undo any previous non-installing. isFirstBoot lacks this requirement.
return false;
}
Slog.i(TAG, "Reviewing whitelisted packages due to "
- + (isFirstBoot ? "[firstBoot]" : "") + (isUpgrade ? "[upgrade]" : ""));
+ + (isFirstBoot ? "[firstBoot]" : "") + (isConsideredUpgrade ? "[upgrade]" : ""));
final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class);
// Install/uninstall system packages per user.
for (int userId : mUm.getUserIds()) {
@@ -185,7 +193,7 @@
final boolean install =
(userWhitelist == null || userWhitelist.contains(pkg.packageName))
&& !pkg.applicationInfo.hiddenUntilInstalled;
- if (isUpgrade && !isFirstBoot && !install) {
+ if (isConsideredUpgrade && !isFirstBoot && !install) {
return; // To be careful, we don’t uninstall apps during OTAs
}
final boolean changed = pmInt.setInstalled(pkg, userId, install);
@@ -245,6 +253,19 @@
}
/**
+ * Whether to ignore OTAs, and therefore not install missing system packages during OTAs.
+ * <p>Note:
+ * If in this mode, old system packages will not be installed on pre-existing users during OTAs.
+ * Any system packages that had not been installed at the time of the user's creation,
+ * due to {@link UserSystemPackageInstaller}'s previous actions, will therefore continue to
+ * remain uninstalled, even if the whitelist (or enforcement mode) now declares that they should
+ * be.
+ */
+ boolean isIgnoreOtaMode() {
+ return isIgnoreOtaMode(getWhitelistMode());
+ }
+
+ /**
* Whether to log a warning concerning potential problems with the user-type package whitelist.
*/
boolean isLogMode() {
@@ -264,6 +285,11 @@
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE) != 0;
}
+ /** See {@link #isIgnoreOtaMode()}. */
+ private static boolean isIgnoreOtaMode(int whitelistMode) {
+ return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA) != 0;
+ }
+
/** See {@link #isLogMode()}. */
private static boolean isLogMode(int whitelistMode) {
return (whitelistMode & USER_TYPE_PACKAGE_WHITELIST_MODE_LOG) != 0;
@@ -536,6 +562,7 @@
pw.print(isEnforceMode(mode) ? " (enforced)" : "");
pw.print(isLogMode(mode) ? " (logged)" : "");
pw.print(isImplicitWhitelistMode(mode) ? " (implicit)" : "");
+ pw.print(isIgnoreOtaMode(mode) ? " (ignore OTAs)" : "");
pw.println();
pw.print(prefix); pw.println("Legend");
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 01f29dc..00779ec 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -67,6 +67,7 @@
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -82,6 +83,7 @@
class RollbackManagerServiceImpl extends IRollbackManager.Stub {
private static final String TAG = "RollbackManager";
+ private static final boolean LOCAL_LOGV = false;
// Rollbacks expire after 14 days.
private static final long DEFAULT_ROLLBACK_LIFETIME_DURATION_MILLIS =
@@ -220,6 +222,9 @@
if (Intent.ACTION_CANCEL_ENABLE_ROLLBACK.equals(intent.getAction())) {
int token = intent.getIntExtra(
PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
+ }
synchronized (mLock) {
for (NewRollback rollback : mNewRollbacks) {
if (rollback.hasToken(token)) {
@@ -269,10 +274,17 @@
String action = intent.getAction();
if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
String packageName = intent.getData().getSchemeSpecificPart();
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "broadcast=ACTION_PACKAGE_REPLACED" + " pkg=" + packageName);
+ }
onPackageReplaced(packageName);
}
if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
String packageName = intent.getData().getSchemeSpecificPart();
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED"
+ + " pkg=" + packageName);
+ }
onPackageFullyRemoved(packageName);
}
}
@@ -359,7 +371,7 @@
*/
private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
String callerPackageName, IntentSender statusReceiver) {
- Slog.i(TAG, "Initiating rollback");
+ Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
Rollback rollback = getRollbackForId(rollbackId);
if (rollback == null) {
@@ -444,6 +456,9 @@
}
void onUnlockUser(int userId) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "onUnlockUser id=" + userId);
+ }
// In order to ensure that no package begins running while a backup or restore is taking
// place, onUnlockUser must remain blocked until all pending backups and restores have
// completed.
@@ -611,6 +626,9 @@
if (!now.isBefore(
rollbackTimestamp
.plusMillis(mRollbackLifetimeDurationInMillis))) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "runExpiration id=" + rollback.info.getRollbackId());
+ }
iter.remove();
rollback.delete(mAppDataRollbackHelper);
} else if (oldest == null || oldest.isAfter(rollbackTimestamp)) {
@@ -675,6 +693,10 @@
*/
private boolean enableRollback(
int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "enableRollback user=" + user + " token=" + token
+ + " path=" + newPackageCodePath.getAbsolutePath());
+ }
// Find the session id associated with this install.
// TODO: It would be nice if package manager or package installer told
@@ -838,6 +860,10 @@
}
private void snapshotUserDataInternal(String packageName, int[] userIds) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "snapshotUserData pkg=" + packageName
+ + " users=" + Arrays.toString(userIds));
+ }
synchronized (mLock) {
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
@@ -854,6 +880,10 @@
private void restoreUserDataInternal(
String packageName, int[] userIds, int appId, String seInfo) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "restoreUserData pkg=" + packageName
+ + " users=" + Arrays.toString(userIds));
+ }
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
@@ -1041,6 +1071,9 @@
@Override
public void onFinished(int sessionId, boolean success) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
+ }
NewRollback newRollback;
synchronized (mLock) {
newRollback = getNewRollbackForPackageSessionLocked(sessionId);
@@ -1070,6 +1103,10 @@
*/
private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
Rollback rollback = newRollback.rollback;
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "completeEnableRollback id="
+ + rollback.info.getRollbackId() + " success=" + success);
+ }
if (!success) {
// The install session was aborted, clean up the pending install.
rollback.delete(mAppDataRollbackHelper);
@@ -1108,6 +1145,9 @@
@GuardedBy("rollback.getLock")
private void makeRollbackAvailable(Rollback rollback) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "makeRollbackAvailable id=" + rollback.info.getRollbackId());
+ }
rollback.makeAvailable();
// TODO(zezeozue): Provide API to explicitly start observing instead
@@ -1280,6 +1320,11 @@
final Rollback rollback;
int parentSessionId = parentSession.getSessionId();
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "createNewRollback id=" + rollbackId
+ + " user=" + userId + " installer=" + installerPackageName);
+ }
+
if (parentSession.isStaged()) {
rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId, userId,
installerPackageName);
diff --git a/services/core/java/com/android/server/testharness/TestHarnessModeService.java b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
index fcf87ee..7786833 100644
--- a/services/core/java/com/android/server/testharness/TestHarnessModeService.java
+++ b/services/core/java/com/android/server/testharness/TestHarnessModeService.java
@@ -165,7 +165,7 @@
Settings.Global.putLong(cr, Settings.Global.ADB_ALLOWED_CONNECTION_TIME, 0);
Settings.Global.putInt(cr, Settings.Global.ADB_ENABLED, 1);
Settings.Global.putInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
- Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+ Settings.Global.putInt(cr, Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 0);
Settings.Global.putInt(
cr,
Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 0930975..7f5b403 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -149,6 +149,6 @@
}
private void enforceSuggestManualTimePermission() {
- mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SET_TIME, "set time");
}
}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
new file mode 100644
index 0000000..23746ac
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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.timezonedetector;
+
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+/**
+ * The real implementation of {@link TimeZoneDetectorStrategy.Callback}.
+ */
+public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategy.Callback {
+
+ private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
+
+ private final Context mContext;
+ private final ContentResolver mCr;
+
+ TimeZoneDetectorCallbackImpl(Context context) {
+ mContext = context;
+ mCr = context.getContentResolver();
+ }
+
+ @Override
+ public boolean isTimeZoneDetectionEnabled() {
+ return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
+ }
+
+ @Override
+ public boolean isDeviceTimeZoneInitialized() {
+ // timezone.equals("GMT") will be true and only true if the time zone was
+ // set to a default value by the system server (when starting, system server
+ // sets the persist.sys.timezone to "GMT" if it's not set). "GMT" is not used by
+ // any code that sets it explicitly (in case where something sets GMT explicitly,
+ // "Etc/GMT" Olson ID would be used).
+
+ String timeZoneId = getDeviceTimeZone();
+ return timeZoneId != null && timeZoneId.length() > 0 && !timeZoneId.equals("GMT");
+ }
+
+ @Override
+ @Nullable
+ public String getDeviceTimeZone() {
+ return SystemProperties.get(TIMEZONE_PROPERTY);
+ }
+
+ @Override
+ public void setDeviceTimeZone(String zoneId) {
+ AlarmManager alarmManager = mContext.getSystemService(AlarmManager.class);
+ alarmManager.setTimeZone(zoneId);
+
+ // TODO Nothing in the platform appears to listen for this. Remove it.
+ Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIMEZONE);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.putExtra("time-zone", zoneId);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
new file mode 100644
index 0000000..558aa9e
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -0,0 +1,115 @@
+/*
+ * 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.timezonedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.ITimeZoneDetectorService;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.FgThread;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Objects;
+
+/**
+ * The implementation of ITimeZoneDetectorService.aidl.
+ */
+public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
+ private static final String TAG = "TimeZoneDetectorService";
+
+ /**
+ * Handles the lifecycle for {@link TimeZoneDetectorService}.
+ */
+ public static class Lifecycle extends SystemService {
+
+ public Lifecycle(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
+
+ // Publish the binder service so it can be accessed from other (appropriately
+ // permissioned) processes.
+ publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
+ }
+ }
+
+ @NonNull private final Context mContext;
+ @NonNull private final Handler mHandler;
+ @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+
+ private static TimeZoneDetectorService create(@NonNull Context context) {
+ final TimeZoneDetectorStrategy timeZoneDetectorStrategy =
+ TimeZoneDetectorStrategy.create(context);
+
+ Handler handler = FgThread.getHandler();
+ ContentResolver contentResolver = context.getContentResolver();
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+ new ContentObserver(handler) {
+ public void onChange(boolean selfChange) {
+ timeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+ }
+ });
+
+ return new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
+ }
+
+ @VisibleForTesting
+ public TimeZoneDetectorService(@NonNull Context context, @NonNull Handler handler,
+ @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
+ mContext = Objects.requireNonNull(context);
+ mHandler = Objects.requireNonNull(handler);
+ mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
+ }
+
+ @Override
+ public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ enforceSetTimeZonePermission();
+ Objects.requireNonNull(timeZoneSuggestion);
+
+ mHandler.post(() -> mTimeZoneDetectorStrategy.suggestPhoneTimeZone(timeZoneSuggestion));
+ }
+
+ @Override
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @Nullable String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+
+ mTimeZoneDetectorStrategy.dumpState(pw);
+ mTimeZoneDetectorStrategy.dumpLogs(new IndentingPrintWriter(pw, " "));
+ }
+
+ private void enforceSetTimeZonePermission() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.SET_TIME_ZONE, "set time zone");
+ }
+}
+
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
new file mode 100644
index 0000000..e24c089
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -0,0 +1,507 @@
+/*
+ * Copyright 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.timezonedetector;
+
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A singleton, stateful time zone detection strategy that is aware of multiple phone devices. It
+ * keeps track of the most recent suggestion from each phone and it uses the best based on a scoring
+ * algorithm. If several phones provide the same score then the phone with the lowest numeric ID
+ * "wins". If the situation changes and it is no longer possible to be confident about the time
+ * zone, phones must submit an empty suggestion in order to "withdraw" their previous suggestion.
+ */
+public class TimeZoneDetectorStrategy {
+
+ /**
+ * Used by {@link TimeZoneDetectorStrategy} to interact with the surrounding service. It can be
+ * faked for tests.
+ */
+ @VisibleForTesting
+ public interface Callback {
+
+ /**
+ * Returns true if automatic time zone detection is enabled in settings.
+ */
+ boolean isTimeZoneDetectionEnabled();
+
+ /**
+ * Returns true if the device has had an explicit time zone set.
+ */
+ boolean isDeviceTimeZoneInitialized();
+
+ /**
+ * Returns the device's currently configured time zone.
+ */
+ String getDeviceTimeZone();
+
+ /**
+ * Sets the device's time zone.
+ */
+ void setDeviceTimeZone(@NonNull String zoneId);
+ }
+
+ static final String LOG_TAG = "TimeZoneDetectorStrategy";
+ static final boolean DBG = false;
+
+ /**
+ * The abstract score for an empty or invalid suggestion.
+ *
+ * Used to score suggestions where there is no zone.
+ */
+ @VisibleForTesting
+ public static final int SCORE_NONE = 0;
+
+ /**
+ * The abstract score for a low quality suggestion.
+ *
+ * Used to score suggestions where:
+ * The suggested zone ID is one of several possibilities, and the possibilities have different
+ * offsets.
+ *
+ * You would have to be quite desperate to want to use this choice.
+ */
+ @VisibleForTesting
+ public static final int SCORE_LOW = 1;
+
+ /**
+ * The abstract score for a medium quality suggestion.
+ *
+ * Used for:
+ * The suggested zone ID is one of several possibilities but at least the possibilities have the
+ * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
+ * switch to DST at the wrong time and (for example) their calendar events.
+ */
+ @VisibleForTesting
+ public static final int SCORE_MEDIUM = 2;
+
+ /**
+ * The abstract score for a high quality suggestion.
+ *
+ * Used for:
+ * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
+ * the info available.
+ */
+ @VisibleForTesting
+ public static final int SCORE_HIGH = 3;
+
+ /**
+ * The abstract score for a highest quality suggestion.
+ *
+ * Used for:
+ * Suggestions that must "win" because they constitute test or emulator zone ID.
+ */
+ @VisibleForTesting
+ public static final int SCORE_HIGHEST = 4;
+
+ /** The threshold at which suggestions are good enough to use to set the device's time zone. */
+ @VisibleForTesting
+ public static final int SCORE_USAGE_THRESHOLD = SCORE_MEDIUM;
+
+ /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+
+ @NonNull
+ private final Callback mCallback;
+
+ /**
+ * A log that records the decisions / decision metadata that affected the device's time zone
+ * (for use during debugging).
+ */
+ @NonNull
+ private final LocalLog mTimeZoneChangesLog = new LocalLog(30);
+
+ /**
+ * A mapping from phoneId to a linked list of time zone suggestions (the head being the latest).
+ * We typically expect one or two entries in this Map: devices will have a small number
+ * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
+ * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+ */
+ @GuardedBy("this")
+ private ArrayMap<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> mSuggestionByPhoneId =
+ new ArrayMap<>();
+
+ /**
+ * The most recent best guess of time zone from all phones. Can be {@code null} to indicate
+ * there would be no current suggestion.
+ */
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion mCurrentSuggestion;
+
+ /**
+ * Creates a new instance of {@link TimeZoneDetectorStrategy}.
+ */
+ public static TimeZoneDetectorStrategy create(Context context) {
+ Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
+ return new TimeZoneDetectorStrategy(timeZoneDetectionServiceHelper);
+ }
+
+ @VisibleForTesting
+ public TimeZoneDetectorStrategy(Callback callback) {
+ mCallback = Objects.requireNonNull(callback);
+ }
+
+ /**
+ * Suggests a time zone for the device, or withdraws a previous suggestion if
+ * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a
+ * specific {@link PhoneTimeZoneSuggestion#getPhoneId() phone}.
+ * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a
+ * suggestion. The service uses suggestions to decide whether to modify the device's time zone
+ * setting and what to set it to.
+ */
+ public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion newSuggestion) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "suggestPhoneTimeZone: newSuggestion=" + newSuggestion);
+ }
+ Objects.requireNonNull(newSuggestion);
+
+ int score = scoreSuggestion(newSuggestion);
+ QualifiedPhoneTimeZoneSuggestion scoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(newSuggestion, score);
+
+ // Record the suggestion against the correct phoneId.
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
+ mSuggestionByPhoneId.get(newSuggestion.getPhoneId());
+ if (suggestions == null) {
+ suggestions = new LinkedList<>();
+ mSuggestionByPhoneId.put(newSuggestion.getPhoneId(), suggestions);
+ }
+ suggestions.addFirst(scoredSuggestion);
+ if (suggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+ suggestions.removeLast();
+ }
+
+ // Now run the competition between the phones' suggestions.
+ doTimeZoneDetection();
+ }
+
+ private static int scoreSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ int score;
+ if (suggestion.getZoneId() == null) {
+ score = SCORE_NONE;
+ } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY
+ || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) {
+ // Handle emulator / test cases : These suggestions should always just be used.
+ score = SCORE_HIGHEST;
+ } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) {
+ score = SCORE_HIGH;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) {
+ // The suggestion may be wrong, but at least the offset should be correct.
+ score = SCORE_MEDIUM;
+ } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) {
+ // The suggestion has a good chance of being wrong.
+ score = SCORE_LOW;
+ } else {
+ throw new AssertionError();
+ }
+ return score;
+ }
+
+ /**
+ * Finds the best available time zone suggestion from all phones. If it is high-enough quality
+ * and automatic time zone detection is enabled then it will be set on the device. The outcome
+ * can be that this service becomes / remains un-opinionated and nothing is set.
+ */
+ @GuardedBy("this")
+ private void doTimeZoneDetection() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = findBestSuggestion();
+ boolean timeZoneDetectionEnabled = mCallback.isTimeZoneDetectionEnabled();
+
+ // Work out what to do with the best suggestion.
+ if (bestSuggestion == null) {
+ // There is no suggestion. Become un-opinionated.
+ if (DBG) {
+ Slog.d(LOG_TAG, "doTimeZoneDetection: No good suggestion."
+ + " bestSuggestion=null"
+ + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+ }
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // Special case handling for uninitialized devices. This should only happen once.
+ String newZoneId = bestSuggestion.suggestion.getZoneId();
+ if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) {
+ Slog.i(LOG_TAG, "doTimeZoneDetection: Device has no time zone set so might set the"
+ + " device to the best available suggestion."
+ + " bestSuggestion=" + bestSuggestion
+ + ", timeZoneDetectionEnabled=" + timeZoneDetectionEnabled);
+
+ mCurrentSuggestion = bestSuggestion;
+ if (timeZoneDetectionEnabled) {
+ setDeviceTimeZone(bestSuggestion.suggestion);
+ }
+ return;
+ }
+
+ boolean suggestionGoodEnough = bestSuggestion.score >= SCORE_USAGE_THRESHOLD;
+ if (!suggestionGoodEnough) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "doTimeZoneDetection: Suggestion not good enough."
+ + " bestSuggestion=" + bestSuggestion);
+ }
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
+ // zone ID.
+ if (newZoneId == null) {
+ Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
+ + " bestSuggestion=" + bestSuggestion);
+ mCurrentSuggestion = null;
+ return;
+ }
+
+ // There is a good suggestion. Store the suggestion and set the device time zone if
+ // settings allow.
+ mCurrentSuggestion = bestSuggestion;
+
+ // Only set the device time zone if time zone detection is enabled.
+ if (!timeZoneDetectionEnabled) {
+ if (DBG) {
+ Slog.d(LOG_TAG, "doTimeZoneDetection: Not setting the time zone because time zone"
+ + " detection is disabled."
+ + " bestSuggestion=" + bestSuggestion);
+ }
+ return;
+ }
+ PhoneTimeZoneSuggestion suggestion = bestSuggestion.suggestion;
+ setDeviceTimeZone(suggestion);
+ }
+
+ private void setDeviceTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) {
+ String currentZoneId = mCallback.getDeviceTimeZone();
+ String newZoneId = suggestion.getZoneId();
+
+ // Paranoia: This should never happen.
+ if (newZoneId == null) {
+ Slog.w(LOG_TAG, "setDeviceTimeZone: Suggested zone is null."
+ + " timeZoneSuggestion=" + suggestion);
+ return;
+ }
+
+ // Avoid unnecessary changes / intents.
+ if (newZoneId.equals(currentZoneId)) {
+ // No need to set the device time zone - the setting is already what we would be
+ // suggesting.
+ if (DBG) {
+ Slog.d(LOG_TAG, "setDeviceTimeZone: No need to change the time zone;"
+ + " device is already set to the suggested zone."
+ + " timeZoneSuggestion=" + suggestion);
+ }
+ return;
+ }
+
+ String msg = "Changing device time zone. currentZoneId=" + currentZoneId
+ + ", timeZoneSuggestion=" + suggestion;
+ if (DBG) {
+ Slog.d(LOG_TAG, msg);
+ }
+ mTimeZoneChangesLog.log(msg);
+ mCallback.setDeviceTimeZone(newZoneId);
+ }
+
+ @GuardedBy("this")
+ @Nullable
+ private QualifiedPhoneTimeZoneSuggestion findBestSuggestion() {
+ QualifiedPhoneTimeZoneSuggestion bestSuggestion = null;
+
+ // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone
+ // and find the best. Note that we deliberately do not look at age: the caller can
+ // rate-limit so age is not a strong indicator of confidence. Instead, the callers are
+ // expected to withdraw suggestions they no longer have confidence in.
+ for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> phoneSuggestions =
+ mSuggestionByPhoneId.valueAt(i);
+ if (phoneSuggestions == null) {
+ // Unexpected
+ continue;
+ }
+ QualifiedPhoneTimeZoneSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+ if (candidateSuggestion == null) {
+ // Unexpected
+ continue;
+ }
+
+ if (bestSuggestion == null) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score > bestSuggestion.score) {
+ bestSuggestion = candidateSuggestion;
+ } else if (candidateSuggestion.score == bestSuggestion.score) {
+ // Tie! Use the suggestion with the lowest phoneId.
+ int candidatePhoneId = candidateSuggestion.suggestion.getPhoneId();
+ int bestPhoneId = bestSuggestion.suggestion.getPhoneId();
+ if (candidatePhoneId < bestPhoneId) {
+ bestSuggestion = candidateSuggestion;
+ }
+ }
+ }
+ return bestSuggestion;
+ }
+
+ /**
+ * Returns the current best suggestion. Not intended for general use: it is used during tests
+ * to check service behavior.
+ */
+ @VisibleForTesting
+ @Nullable
+ public synchronized QualifiedPhoneTimeZoneSuggestion findBestSuggestionForTests() {
+ return findBestSuggestion();
+ }
+
+ /**
+ * Called when the has been a change to the automatic time zone detection setting.
+ */
+ @VisibleForTesting
+ public synchronized void handleTimeZoneDetectionChange() {
+ if (DBG) {
+ Slog.d(LOG_TAG, "handleTimeZoneDetectionChange() called");
+ }
+ if (mCallback.isTimeZoneDetectionEnabled()) {
+ // When the user enabled time zone detection, run the time zone detection and change the
+ // device time zone if possible.
+ doTimeZoneDetection();
+ }
+ }
+
+ /**
+ * Dumps any logs held to the supplied writer.
+ */
+ public synchronized void dumpLogs(IndentingPrintWriter ipw) {
+ ipw.println("TimeZoneDetectorStrategy:");
+
+ ipw.increaseIndent(); // level 1
+
+ ipw.println("Time zone change log:");
+ ipw.increaseIndent(); // level 2
+ mTimeZoneChangesLog.dump(ipw);
+ ipw.decreaseIndent(); // level 2
+
+ ipw.println("Phone suggestion history:");
+ ipw.increaseIndent(); // level 2
+ for (Map.Entry<Integer, LinkedList<QualifiedPhoneTimeZoneSuggestion>> entry
+ : mSuggestionByPhoneId.entrySet()) {
+ ipw.println("Phone " + entry.getKey());
+
+ ipw.increaseIndent(); // level 3
+ for (QualifiedPhoneTimeZoneSuggestion suggestion : entry.getValue()) {
+ ipw.println(suggestion);
+ }
+ ipw.decreaseIndent(); // level 3
+ }
+ ipw.decreaseIndent(); // level 2
+ ipw.decreaseIndent(); // level 1
+ }
+
+ /**
+ * Dumps internal state such as field values.
+ */
+ public synchronized void dumpState(PrintWriter pw) {
+ pw.println("mCurrentSuggestion=" + mCurrentSuggestion);
+ pw.println("mCallback.isTimeZoneDetectionEnabled()="
+ + mCallback.isTimeZoneDetectionEnabled());
+ pw.println("mCallback.isDeviceTimeZoneInitialized()="
+ + mCallback.isDeviceTimeZoneInitialized());
+ pw.println("mCallback.getDeviceTimeZone()="
+ + mCallback.getDeviceTimeZone());
+ pw.flush();
+ }
+
+ /**
+ * A method used to inspect service state during tests. Not intended for general use.
+ */
+ @VisibleForTesting
+ public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int phoneId) {
+ LinkedList<QualifiedPhoneTimeZoneSuggestion> suggestions =
+ mSuggestionByPhoneId.get(phoneId);
+ if (suggestions == null) {
+ return null;
+ }
+ return suggestions.getFirst();
+ }
+
+ /**
+ * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata.
+ */
+ @VisibleForTesting
+ public static class QualifiedPhoneTimeZoneSuggestion {
+
+ @VisibleForTesting
+ public final PhoneTimeZoneSuggestion suggestion;
+
+ /**
+ * The score the suggestion has been given. This can be used to rank against other
+ * suggestions of the same type.
+ */
+ @VisibleForTesting
+ public final int score;
+
+ @VisibleForTesting
+ public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) {
+ this.suggestion = suggestion;
+ this.score = score;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o;
+ return score == that.score
+ && suggestion.equals(that.suggestion);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(score, suggestion);
+ }
+
+ @Override
+ public String toString() {
+ return "QualifiedPhoneTimeZoneSuggestion{"
+ + "suggestion=" + suggestion
+ + ", score=" + score
+ + '}';
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 45e3c68..db94cf1 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -73,7 +73,9 @@
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.protolog.common.ProtoLog;
import java.io.PrintWriter;
@@ -617,7 +619,7 @@
continue;
}
- stack.findTaskLocked(r, mTmpFindTaskResult);
+ mTmpFindTaskResult.process(r, stack);
// It is possible to have tasks in multiple stacks with the same root affinity, so
// we should keep looking after finding an affinity match to see if there is a
// better match in another stack. Also, task affinity isn't a good enough reason
@@ -1190,12 +1192,11 @@
}
boolean isUidPresent(int uid) {
- for (ActivityStack stack : mStacks) {
- if (stack.isUidPresent(uid)) {
- return true;
- }
- }
- return false;
+ final PooledPredicate p = PooledLambda.obtainPredicate(
+ ActivityRecord::isUid, PooledLambda.__(ActivityRecord.class), uid);
+ final boolean isUidPresent = mDisplayContent.getActivity(p) != null;
+ p.recycle();
+ return isUidPresent;
}
/**
@@ -1291,12 +1292,16 @@
/** Update and get all UIDs that are present on the display and have access to it. */
IntArray getPresentUIDs() {
mDisplayAccessUIDs.clear();
- for (ActivityStack stack : mStacks) {
- stack.getPresentUIDs(mDisplayAccessUIDs);
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(ActivityDisplay::addActivityUid,
+ PooledLambda.__(ActivityRecord.class), mDisplayAccessUIDs);
+ mDisplayContent.forAllActivities(c);
+ c.recycle();
return mDisplayAccessUIDs;
}
+ private static void addActivityUid(ActivityRecord r, IntArray uids) {
+ uids.add(r.getUid());
+ }
/**
* Checks if system decorations should be shown on this display.
*
@@ -1449,22 +1454,16 @@
return null;
}
- final ArrayList<Task> tasks = mHomeStack.getAllTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = tasks.get(taskNdx);
- if (!task.isActivityTypeHome()) {
- continue;
- }
+ final PooledPredicate p = PooledLambda.obtainPredicate(
+ ActivityDisplay::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
+ userId);
+ final ActivityRecord r = mHomeStack.getActivity(p);
+ p.recycle();
+ return r;
+ }
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.isActivityTypeHome()
- && ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
- return r;
- }
- }
- }
- return null;
+ private static boolean isHomeActivityForUser(ActivityRecord r, int userId) {
+ return r.isActivityTypeHome() && (userId == UserHandle.USER_ALL || r.mUserId == userId);
}
boolean isSleeping() {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 9fa5d9f1..a593ef8 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -546,13 +546,7 @@
/** @return {@code true} if the given task has an activity will be drawn. */
private static boolean hasActivityToBeDrawn(Task t) {
- for (int i = t.getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord r = t.getChildAt(i);
- if (r.mVisibleRequested && !r.mDrawn && !r.finishing) {
- return true;
- }
- }
- return false;
+ return t.forAllActivities((r) -> r.mVisibleRequested && !r.mDrawn && !r.finishing);
}
private void checkVisibility(Task t, ActivityRecord r) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 3de3578..6af5025 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -301,6 +301,9 @@
import com.android.internal.content.ReferrerIntent;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.am.AppTimeTracker;
@@ -695,12 +698,6 @@
}
}
- void dump(PrintWriter pw, String prefix) {
- }
-
- /**
- * Copied from old AppWindowToken.
- */
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final long now = SystemClock.uptimeMillis();
@@ -940,6 +937,10 @@
}
}
+ void setAppTimeTracker(AppTimeTracker att) {
+ appTimeTracker = att;
+ }
+
/** Update the saved state of an activity. */
void setSavedState(@Nullable Bundle savedState) {
mIcicle = savedState;
@@ -1167,6 +1168,15 @@
super.onParentChanged(newParent, oldParent);
+ if (isPersistable()) {
+ if (oldTask != null) {
+ mAtmService.notifyTaskPersisterLocked(oldTask, false);
+ }
+ if (newTask != null) {
+ mAtmService.notifyTaskPersisterLocked(newTask, false);
+ }
+ }
+
if (oldParent == null && newParent != null) {
// First time we are adding the activity to the system.
mVoiceInteraction = newTask.voiceSession != null;
@@ -1261,12 +1271,8 @@
if (prevDc.mFocusedApp == this) {
prevDc.setFocusedApp(null);
- final ActivityStack stack = dc.getTopStack();
- if (stack != null) {
- final Task task = stack.getTopChild();
- if (task != null && task.getTopChild() == this) {
- dc.setFocusedApp(this);
- }
+ if (dc.getTopMostActivity() == this) {
+ dc.setFocusedApp(this);
}
}
@@ -1520,7 +1526,13 @@
hasBeenLaunched = false;
mStackSupervisor = supervisor;
- taskAffinity = aInfo.taskAffinity;
+ // b/35954083: Limit task affinity to uid to avoid various issues associated with sharing
+ // affinity across uids.
+ final String uid = Integer.toString(info.applicationInfo.uid);
+ if (info.taskAffinity != null && !info.taskAffinity.startsWith(uid)) {
+ info.taskAffinity = uid + ":" + info.taskAffinity;
+ }
+ taskAffinity = info.taskAffinity;
stateNotNeeded = (aInfo.flags & FLAG_STATE_NOT_NEEDED) != 0;
nonLocalizedLabel = aInfo.nonLocalizedLabel;
labelRes = aInfo.labelRes;
@@ -1798,7 +1810,7 @@
// task snapshot starting window.
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
}
- return snapshot == null ? STARTING_WINDOW_TYPE_SPLASH_SCREEN
+ return snapshot == null ? STARTING_WINDOW_TYPE_NONE
: snapshotOrientationSameAsTask(snapshot) || fromRecents
? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
@@ -2206,7 +2218,7 @@
OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
}
- boolean isAlwaysFocusable() {
+ private boolean isAlwaysFocusable() {
return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}
@@ -2263,23 +2275,28 @@
return true;
}
+ void finishIfSubActivity(ActivityRecord parent, String otherResultWho, int otherRequestCode) {
+ if (resultTo != parent
+ || requestCode != otherRequestCode
+ || !Objects.equals(resultWho, otherResultWho)) return;
+
+ finishIfPossible("request-sub", false /* oomAdj */);
+ }
+
/** Finish all activities in the task with the same affinity as this one. */
- void finishActivityAffinity() {
- final ArrayList<ActivityRecord> activities = task.mChildren;
- for (int index = activities.indexOf(this); index >= 0; --index) {
- final ActivityRecord cur = activities.get(index);
- if (!Objects.equals(cur.taskAffinity, taskAffinity)) {
- break;
- }
- cur.finishIfPossible("request-affinity", true /* oomAdj */);
- }
+ boolean finishIfSameAffinity(ActivityRecord r) {
+ // End search once we get to the activity that doesn't have the same affinity.
+ if (!Objects.equals(r.taskAffinity, taskAffinity)) return true;
+
+ r.finishIfPossible("request-affinity", true /* oomAdj */);
+ return false;
}
/**
* Sets the result for activity that started this one, clears the references to activities
* started for result from this one, and clears new intents.
*/
- void finishActivityResults(int resultCode, Intent resultData) {
+ private void finishActivityResults(int resultCode, Intent resultData) {
// Send the result if needed
if (resultTo != null) {
if (DEBUG_RESULTS) {
@@ -2378,14 +2395,12 @@
final Task task = getTask();
EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),
task.mTaskId, shortComponentName, reason);
- final ArrayList<ActivityRecord> activities = task.mChildren;
- final int index = activities.indexOf(this);
- if (index < (task.getChildCount() - 1)) {
+ ActivityRecord next = task.getActivityAbove(this);
+ if (next != null) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
- final ActivityRecord next = task.getChildAt(index + 1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
@@ -2403,7 +2418,7 @@
} else {
// Only move the next stack to top in its display.
final ActivityDisplay display = stack.getDisplay();
- final ActivityRecord next = display.topRunningActivity();
+ next = display.topRunningActivity();
if (next != null) {
display.positionChildAtTop(next.getActivityStack(),
false /* includingParents */, "finish-display-top");
@@ -2413,7 +2428,8 @@
finishActivityResults(resultCode, resultData);
- final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
+ final boolean endTask = task.getActivityBelow(this) == null
+ && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
if (isState(RESUMED)) {
if (endTask) {
@@ -2473,16 +2489,13 @@
// sync with the activity visibility being set for this finishing activity above.
// In this case, we can set the visibility of all the task overlay activities when
// we detect the last one is finishing to keep them in sync.
- if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
- for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
- final ActivityRecord taskOverlay = task.getChildAt(i);
- if (!taskOverlay.mTaskOverlay) {
- continue;
- }
- taskOverlay.prepareActivityHideTransitionAnimation(transit);
- }
+ if (task.onlyHasTaskOverlayActivities(false /* includeFinishing */)) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityRecord::prepareActivityHideTransitionAnimationIfOvarlay,
+ PooledLambda.__(ActivityRecord.class), transit);
+ task.forAllActivities(c);
+ c.recycle();
}
-
return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this);
@@ -2494,6 +2507,12 @@
}
}
+ private void prepareActivityHideTransitionAnimationIfOvarlay(int transit) {
+ if (mTaskOverlay) {
+ prepareActivityHideTransitionAnimation(transit);
+ }
+ }
+
private void prepareActivityHideTransitionAnimation(int transit) {
final DisplayContent dc = getDisplay().mDisplayContent;
dc.prepareAppTransition(transit, false);
@@ -3265,15 +3284,16 @@
* immediately finishes after, so we have to transfer T to M.
*/
void transferStartingWindowFromHiddenAboveTokenIfNeeded() {
- for (int i = task.mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord fromActivity = task.mChildren.get(i);
- if (fromActivity == this) {
- return;
- }
- if (!fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token)) {
- return;
- }
- }
+ final PooledFunction p = PooledLambda.obtainFunction(ActivityRecord::transferStartingWindow,
+ this, PooledLambda.__(ActivityRecord.class));
+ task.forAllActivities(p);
+ p.recycle();
+ }
+
+ private boolean transferStartingWindow(ActivityRecord fromActivity) {
+ if (fromActivity == this) return true;
+
+ return !fromActivity.mVisibleRequested && transferStartingWindow(fromActivity.token);
}
void checkKeyguardFlagsChanged() {
@@ -3345,7 +3365,7 @@
if (!inPinnedWindowingMode() && (mShowWhenLocked || containsShowWhenLockedWindow())) {
return true;
} else if (mInheritShownWhenLocked) {
- final ActivityRecord r = getActivityBelow();
+ final ActivityRecord r = task.getActivityBelow(this);
return r != null && !r.inPinnedWindowingMode() && (r.mShowWhenLocked
|| r.containsShowWhenLockedWindow());
} else {
@@ -3370,19 +3390,6 @@
true /* topToBottom */);
}
- /**
- * @return an {@link ActivityRecord} of the activity below this activity, or {@code null} if no
- * such activity exists.
- */
- @Nullable
- private ActivityRecord getActivityBelow() {
- final int pos = task.mChildren.indexOf(this);
- if (pos == -1) {
- throw new IllegalStateException("Activity not found in its task");
- }
- return pos == 0 ? null : task.getChildAt(pos - 1);
- }
-
WindowState getImeTargetBelowWindow(WindowState w) {
final int index = mChildren.indexOf(w);
if (index > 0) {
@@ -3426,7 +3433,8 @@
}
@Override
- boolean forAllActivities(Function<ActivityRecord, Boolean> callback) {
+ boolean forAllActivities(
+ Function<ActivityRecord, Boolean> callback, boolean traverseTopToBottom) {
return callback.apply(this);
}
@@ -3603,7 +3611,9 @@
clearOptionsLocked(false /* withAbort */);
} else {
// This will clear the options for all the ActivityRecords for this Task.
- task.clearAllPendingOptions();
+ task.forAllActivities((r) -> {
+ r.clearOptionsLocked(false /* withAbort */);
+ });
}
}
}
@@ -4596,16 +4606,15 @@
}
// Check if position in task allows to become paused
- final int positionInTask = task.mChildren.indexOf(this);
- if (positionInTask == -1) {
+ if (!task.hasChild(this)) {
throw new IllegalStateException("Activity not found in its task");
}
- if (positionInTask == task.getChildCount() - 1) {
+ final ActivityRecord activityAbove = task.getActivityAbove(this);
+ if (activityAbove == null) {
// It's the topmost activity in the task - should become resumed now
return true;
}
// Check if activity above is finishing now and this one becomes the topmost in task.
- final ActivityRecord activityAbove = task.getChildAt(positionInTask + 1);
if (activityAbove.finishing) {
return true;
}
@@ -4661,7 +4670,7 @@
stopped = false;
if (isActivityTypeHome()) {
- mStackSupervisor.updateHomeProcess(task.getChildAt(0).app);
+ mStackSupervisor.updateHomeProcess(task.getBottomMostActivity().app);
}
if (nowVisible) {
@@ -5288,6 +5297,10 @@
&& mAtmService.mAmInternal.isUserRunning(mUserId, 0 /* flags */));
}
+ boolean canBeTopRunning() {
+ return !finishing && okToShowLocked();
+ }
+
/**
* This method will return true if the activity is either visible, is becoming visible, is
* currently pausing, or is resumed.
@@ -5319,13 +5332,12 @@
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
- if (r == null) {
+ if (r == null || r.getParent() == null) {
return INVALID_TASK_ID;
}
final Task task = r.task;
- final int activityNdx = task.mChildren.indexOf(r);
- if (activityNdx < 0
- || (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) {
+ if (onlyRoot && r.compareTo(task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)) > 0) {
return INVALID_TASK_ID;
}
return task.mTaskId;
@@ -7216,6 +7228,10 @@
return info.applicationInfo.uid;
}
+ boolean isUid(int uid) {
+ return info.applicationInfo.uid == uid;
+ }
+
int getPid() {
return app != null ? app.getPid() : 0;
}
@@ -7279,13 +7295,8 @@
if (task == null) {
return false;
}
- final ActivityRecord rootActivity = task.getRootActivity();
- if (rootActivity != null) {
- return this == rootActivity;
- }
- // No non-finishing activity found. In this case the bottom-most activity is considered to
- // be the root.
- return task.getChildAt(0) == this;
+ final ActivityRecord rootActivity = task.getRootActivity(true);
+ return this == rootActivity;
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 6ddbb0d..bb3126b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -76,12 +76,10 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ADD_REMOVE;
@@ -107,7 +105,6 @@
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS;
import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
@@ -143,12 +140,10 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
-import android.net.Uri;
import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
@@ -162,7 +157,6 @@
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
import android.util.DisplayMetrics;
-import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -178,7 +172,10 @@
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -189,14 +186,12 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.function.Consumer;
/**
* State and management of a single stack of activities.
*/
-class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget,
- ConfigurationContainerListener {
+class ActivityStack extends WindowContainer<Task> implements BoundsAnimationTarget {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_ATM;
static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_APP = TAG + POSTFIX_APP;
@@ -499,7 +494,7 @@
case DESTROY_ACTIVITIES_MSG: {
ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
synchronized (mService.mGlobalLock) {
- destroyActivitiesLocked(args.mOwner, args.mReason);
+ destroyActivities(args.mOwner, args.mReason);
}
} break;
case TRANSLUCENT_TIMEOUT_MSG: {
@@ -514,13 +509,226 @@
private static final ResetTargetTaskHelper sResetTargetTaskHelper = new ResetTargetTaskHelper();
private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
new EnsureActivitiesVisibleHelper(this);
+ private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
+ new EnsureVisibleActivitiesConfigHelper();
+ private class EnsureVisibleActivitiesConfigHelper {
+ private boolean mUpdateConfig;
+ private boolean mPreserveWindow;
+ private boolean mBehindFullscreen;
- int numActivities() {
- int count = 0;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- count += getChildAt(taskNdx).getChildCount();
+ void reset(boolean preserveWindow) {
+ mPreserveWindow = preserveWindow;
+ mUpdateConfig = false;
+ mBehindFullscreen = false;
}
- return count;
+
+ void process(ActivityRecord start, boolean preserveWindow) {
+ if (start == null || !start.mVisibleRequested) {
+ return;
+ }
+ reset(preserveWindow);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ EnsureVisibleActivitiesConfigHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(f, start.getTask(), true /*includeBoundary*/,
+ true /*traverseTopToBottom*/);
+ f.recycle();
+
+ if (mUpdateConfig) {
+ // Ensure the resumed state of the focus activity if we updated the configuration of
+ // any activity.
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+ }
+ }
+
+ boolean processActivity(ActivityRecord r) {
+ mUpdateConfig |= r.ensureActivityConfiguration(0 /*globalChanges*/, mPreserveWindow);
+ mBehindFullscreen |= r.occludesParent();
+ return mBehindFullscreen;
+ }
+ }
+
+ private final CheckBehindFullscreenActivityHelper mCheckBehindFullscreenActivityHelper =
+ new CheckBehindFullscreenActivityHelper();
+ private class CheckBehindFullscreenActivityHelper {
+ private boolean mAboveTop;
+ private boolean mBehindFullscreenActivity;
+ private ActivityRecord mToCheck;
+ private Consumer<ActivityRecord> mHandleBehindFullscreenActivity;
+ private boolean mHandlingOccluded;
+
+ private void reset(ActivityRecord toCheck,
+ Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+ mToCheck = toCheck;
+ mHandleBehindFullscreenActivity = handleBehindFullscreenActivity;
+ mAboveTop = true;
+ mBehindFullscreenActivity = false;
+
+ if (!shouldBeVisible(null)) {
+ // The stack is not visible, so no activity in it should be displaying a starting
+ // window. Mark all activities below top and behind fullscreen.
+ mAboveTop = false;
+ mBehindFullscreenActivity = true;
+ }
+
+ mHandlingOccluded = mToCheck == null && mHandleBehindFullscreenActivity != null;
+ }
+
+ boolean process(ActivityRecord toCheck,
+ Consumer<ActivityRecord> handleBehindFullscreenActivity) {
+ reset(toCheck, handleBehindFullscreenActivity);
+
+ if (!mHandlingOccluded && mBehindFullscreenActivity) {
+ return true;
+ }
+
+ final ActivityRecord topActivity = topRunningActivityLocked();
+ final PooledFunction f = PooledLambda.obtainFunction(
+ CheckBehindFullscreenActivityHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class), topActivity);
+ forAllActivities(f);
+ f.recycle();
+
+ return mBehindFullscreenActivity;
+ }
+
+ private boolean processActivity(ActivityRecord r, ActivityRecord topActivity) {
+ if (mAboveTop) {
+ if (r == topActivity) {
+ if (r == mToCheck) {
+ // It is the top activity in a visible stack.
+ mBehindFullscreenActivity = false;
+ return true;
+ }
+ mAboveTop = false;
+ }
+ mBehindFullscreenActivity |= r.occludesParent();
+ return false;
+ }
+
+ if (mHandlingOccluded) {
+ mHandleBehindFullscreenActivity.accept(r);
+ } else if (r == mToCheck) {
+ return true;
+ } else if (mBehindFullscreenActivity) {
+ // It is occluded before {@param toCheck} is found.
+ return true;
+ }
+ mBehindFullscreenActivity |= r.occludesParent();
+ return false;
+ }
+ }
+
+ // TODO: Can we just loop through WindowProcessController#mActivities instead of doing this?
+ private final RemoveHistoryRecordsForApp mRemoveHistoryRecordsForApp =
+ new RemoveHistoryRecordsForApp();
+ private class RemoveHistoryRecordsForApp {
+ private boolean mHasVisibleActivities;
+ private boolean mIsProcessRemoved;
+ private WindowProcessController mApp;
+ private ArrayList<ActivityRecord> mToRemove = new ArrayList<>();
+
+ boolean process(WindowProcessController app) {
+ mToRemove.clear();
+ mHasVisibleActivities = false;
+ mApp = app;
+ mIsProcessRemoved = app.isRemoved();
+ if (mIsProcessRemoved) {
+ // The package of the died process should be force-stopped, so make its activities
+ // as finishing to prevent the process from being started again if the next top
+ // (or being visible) activity also resides in the same process.
+ app.makeFinishingForProcessRemoved();
+ }
+
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RemoveHistoryRecordsForApp::addActivityToRemove, this,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(c);
+ c.recycle();
+
+ while (!mToRemove.isEmpty()) {
+ processActivity(mToRemove.remove(0));
+ }
+
+ mApp = null;
+ return mHasVisibleActivities;
+ }
+
+ private void addActivityToRemove(ActivityRecord r) {
+ if (r.app == mApp) {
+ mToRemove.add(r);
+ }
+ }
+
+ private void processActivity(ActivityRecord r) {
+ if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Record " + r + ": app=" + r.app);
+
+ if (r.app != mApp) {
+ return;
+ }
+ if (r.isVisible() || r.mVisibleRequested) {
+ // While an activity launches a new activity, it's possible that the old
+ // activity is already requested to be hidden (mVisibleRequested=false), but
+ // this visibility is not yet committed, so isVisible()=true.
+ mHasVisibleActivities = true;
+ }
+ final boolean remove;
+ if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+ || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+ && r.launchCount < 3 && !r.finishing) {
+ // If the process crashed during a resize, always try to relaunch it, unless
+ // it has failed more than twice. Skip activities that's already finishing
+ // cleanly by itself.
+ remove = false;
+ } else if ((!r.hasSavedState() && !r.stateNotNeeded
+ && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
+ // Don't currently have state for the activity, or
+ // it is finishing -- always remove it.
+ remove = true;
+ } else if (!r.mVisibleRequested && r.launchCount > 2
+ && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
+ // We have launched this activity too many times since it was
+ // able to run, so give up and remove it.
+ // (Note if the activity is visible, we don't remove the record.
+ // We leave the dead window on the screen but the process will
+ // not be restarted unless user explicitly tap on it.)
+ remove = true;
+ } else {
+ // The process may be gone, but the activity lives on!
+ remove = false;
+ }
+ if (remove) {
+ if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
+ "Removing activity " + r + " from stack "
+ + ": hasSavedState=" + r.hasSavedState()
+ + " stateNotNeeded=" + r.stateNotNeeded
+ + " finishing=" + r.finishing
+ + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
+ if (!r.finishing || mIsProcessRemoved) {
+ Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
+ EventLogTags.writeWmFinishActivity(r.mUserId,
+ System.identityHashCode(r), r.getTask().mTaskId,
+ r.shortComponentName, "proc died without state saved");
+ }
+ } else {
+ // We have the current state for this activity, so
+ // it can be restarted later when needed.
+ if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
+ if (DEBUG_APP) Slog.v(TAG_APP,
+ "Clearing app during removeHistory for activity " + r);
+ r.app = null;
+ // Set nowVisible to previous visible state. If the app was visible while
+ // it died, we leave the dead window on screen so it's basically visible.
+ // This is needed when user later tap on the dead window, we need to stop
+ // other apps when user transfers focus to the restarted activity.
+ r.nowVisible = r.mVisibleRequested;
+ }
+ r.cleanUp(true /* cleanServices */, true /* setState */);
+ if (remove) {
+ r.removeFromHistory("appDied");
+ }
+ }
}
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
@@ -941,7 +1149,7 @@
}
}
- boolean updateBoundsAllowed(Rect bounds) {
+ private boolean updateBoundsAllowed(Rect bounds) {
if (!mUpdateBoundsDeferred) {
return true;
}
@@ -954,7 +1162,7 @@
return false;
}
- boolean updateDisplayedBoundsAllowed(Rect bounds) {
+ private boolean updateDisplayedBoundsAllowed(Rect bounds) {
if (!mUpdateBoundsDeferred) {
return true;
}
@@ -971,47 +1179,29 @@
return topRunningActivityLocked(false /* focusableOnly */);
}
- void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
- outActivities.clear();
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- getChildAt(taskNdx).getAllRunningVisibleActivitiesLocked(outActivities);
- }
- }
-
ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- ActivityRecord r = getChildAt(taskNdx).topRunningActivityLocked();
- if (r != null && (!focusableOnly || r.isFocusable())) {
- return r;
- }
+ // Split into 2 to avoid object creation due to variable capture.
+ if (focusableOnly) {
+ return getActivity((r) -> r.canBeTopRunning() && r.isFocusable());
+ } else {
+ return getActivity(ActivityRecord::canBeTopRunning);
}
- return null;
}
- ActivityRecord topRunningNonOverlayTaskActivity() {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (!r.finishing && !r.mTaskOverlay) {
- return r;
- }
- }
- }
- return null;
+ private ActivityRecord topRunningNonOverlayTaskActivity() {
+ return getActivity((r) -> (r.canBeTopRunning() && !r.mTaskOverlay));
}
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
- return r;
- }
- }
- }
- return null;
+ final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isTopRunningNonDelayed
+ , PooledLambda.__(ActivityRecord.class), notTop);
+ final ActivityRecord r = getActivity(p);
+ p.recycle();
+ return r;
+ }
+
+ private static boolean isTopRunningNonDelayed(ActivityRecord r, ActivityRecord notTop) {
+ return !r.delayedResume && r != notTop && r.canBeTopRunning();
}
/**
@@ -1024,30 +1214,19 @@
* @return Returns the HistoryRecord of the next activity on the stack.
*/
final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- Task task = getChildAt(taskNdx);
- if (task.mTaskId == taskId) {
- continue;
- }
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- // Note: the taskId check depends on real taskId fields being non-zero
- if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
- return r;
- }
- }
- }
- return null;
+ final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isTopRunning,
+ PooledLambda.__(ActivityRecord.class), taskId, token);
+ final ActivityRecord r = getActivity(p);
+ p.recycle();
+ return r;
+ }
+
+ private static boolean isTopRunning(ActivityRecord r, int taskId, IBinder notTop) {
+ return r.getTask().mTaskId == taskId && r.appToken != notTop && r.canBeTopRunning();
}
ActivityRecord getTopNonFinishingActivity() {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final ActivityRecord r = getChildAt(taskNdx).getTopNonFinishingActivity();
- if (r != null) {
- return r;
- }
- }
- return null;
+ return getTopActivity(false /*includeFinishing*/, true /*includeOverlays*/);
}
final Task topTask() {
@@ -1087,35 +1266,6 @@
return null;
}
- boolean isInStackLocked(Task task) {
- return mChildren.contains(task);
- }
-
- /** Checks if there are tasks with specific UID in the stack. */
- boolean isUidPresent(int uid) {
- for (int j = getChildCount() - 1; j >= 0; --j) {
- final Task task = getChildAt(j);
- for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
- final ActivityRecord r = task.getChildAt(i);
- if (r.getUid() == uid) {
- return true;
- }
- }
- }
- return false;
- }
-
- /** Get all UIDs that are present in the stack. */
- void getPresentUIDs(IntArray presentUIDs) {
- for (int j = getChildCount() - 1; j >= 0; --j) {
- final Task task = getChildAt(j);
- for (int i = task.getChildCount() - 1; i >= 0 ; --i) {
- final ActivityRecord r = task.getChildAt(i);
- presentUIDs.add(r.getUid());
- }
- }
- }
-
/** @return true if the stack can only contain one task */
boolean isSingleTaskInstance() {
final ActivityDisplay display = getDisplay();
@@ -1232,138 +1382,6 @@
return display != null && !display.isRemoved();
}
- /**
- * Returns the top activity in any existing task matching the given Intent in the input result.
- * Returns null if no such task is found.
- */
- void findTaskLocked(ActivityRecord target, FindTaskResult result) {
- Intent intent = target.intent;
- ActivityInfo info = target.info;
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
- final int userId = UserHandle.getUserId(info.applicationInfo.uid);
- boolean isDocument = intent != null & intent.isDocument();
- // If documentData is non-null then it must match the existing task data.
- Uri documentData = isDocument ? intent.getData() : null;
-
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- if (task.voiceSession != null) {
- // We never match voice sessions; those always run independently.
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
- continue;
- }
- if (task.mUserId != userId) {
- // Looking for a different task.
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
- continue;
- }
-
- // Overlays should not be considered as the task's logical top activity.
- final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
- if (r == null || r.finishing || r.mUserId != userId ||
- r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
- continue;
- }
- if (!r.hasCompatibleActivityType(target)) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
- continue;
- }
-
- final Intent taskIntent = task.intent;
- final Intent affinityIntent = task.affinityIntent;
- final boolean taskIsDocument;
- final Uri taskDocumentData;
- if (taskIntent != null && taskIntent.isDocument()) {
- taskIsDocument = true;
- taskDocumentData = taskIntent.getData();
- } else if (affinityIntent != null && affinityIntent.isDocument()) {
- taskIsDocument = true;
- taskDocumentData = affinityIntent.getData();
- } else {
- taskIsDocument = false;
- taskDocumentData = null;
- }
-
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
- + (task.realActivity != null ? task.realActivity.flattenToShortString() : "")
- + "/aff=" + r.getTask().rootAffinity + " to new cls="
- + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
- // TODO Refactor to remove duplications. Check if logic can be simplified.
- if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
- && Objects.equals(documentData, taskDocumentData)) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
- //dump();
- if (DEBUG_TASKS) Slog.d(TAG_TASKS,
- "For Intent " + intent + " bringing to top: " + r.intent);
- result.mRecord = r;
- result.mIdealMatch = true;
- break;
- } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
- affinityIntent.getComponent().compareTo(cls) == 0 &&
- Objects.equals(documentData, taskDocumentData)) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
- //dump();
- if (DEBUG_TASKS) Slog.d(TAG_TASKS,
- "For Intent " + intent + " bringing to top: " + r.intent);
- result.mRecord = r;
- result.mIdealMatch = true;
- break;
- } else if (!isDocument && !taskIsDocument
- && result.mRecord == null && task.rootAffinity != null) {
- if (task.rootAffinity.equals(target.taskAffinity)) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
- // It is possible for multiple tasks to have the same root affinity especially
- // if they are in separate stacks. We save off this candidate, but keep looking
- // to see if there is a better candidate.
- result.mRecord = r;
- result.mIdealMatch = false;
- }
- } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
- }
- }
-
- /**
- * Returns the first activity (starting from the top of the stack) that
- * is the same as the given activity. Returns null if no such activity
- * is found.
- */
- ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
- boolean compareIntentFilters) {
- ComponentName cls = intent.getComponent();
- if (info.targetActivity != null) {
- cls = new ComponentName(info.packageName, info.targetActivity);
- }
- final int userId = UserHandle.getUserId(info.applicationInfo.uid);
-
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (!r.okToShowLocked()) {
- continue;
- }
- if (!r.finishing && r.mUserId == userId) {
- if (compareIntentFilters) {
- if (r.intent.filterEquals(intent)) {
- return r;
- }
- } else {
- if (r.intent.getComponent().equals(cls)) {
- return r;
- }
- }
- }
- }
- }
-
- return null;
- }
-
// TODO: Should each user have there own stacks?
@Override
void switchUser(int userId) {
@@ -1401,35 +1419,13 @@
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- r.setSleeping(false);
- }
- }
+ forAllActivities((Consumer<ActivityRecord>) (r) -> r.setSleeping(false));
if (mPausingActivity != null) {
Slog.d(TAG, "awakeFromSleepingLocked: previously pausing activity didn't pause");
activityPausedLocked(mPausingActivity.appToken, true);
}
}
- void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
- final String packageName = aInfo.packageName;
- final int userId = UserHandle.getUserId(aInfo.uid);
-
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord ar = task.getChildAt(activityNdx);
-
- if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
- ar.updateApplicationInfo(aInfo);
- }
- }
- }
- }
-
void checkReadyForSleep() {
if (shouldSleepActivities() && goToSleepIfPossible(false /* shuttingDown */)) {
mStackSupervisor.checkReadyForSleepLocked(true /* allowDelay */);
@@ -1495,15 +1491,11 @@
// Make sure any paused or stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
- r.setSleeping(true);
- }
+ forAllActivities((r) -> {
+ if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
+ r.setSleeping(true);
}
- }
+ });
}
private boolean containsActivityFromStack(List<ActivityRecord> rs) {
@@ -1779,31 +1771,32 @@
if (!isAttached() || mForceHidden) {
return true;
}
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
+ final PooledPredicate p = PooledLambda.obtainPredicate(ActivityStack::isOpaqueActivity,
+ PooledLambda.__(ActivityRecord.class), starting);
+ final ActivityRecord opaque = getActivity(p);
+ p.recycle();
+ return opaque == null;
+ }
- if (r.finishing) {
- // We don't factor in finishing activities when determining translucency since
- // they will be gone soon.
- continue;
- }
-
- if (!r.visibleIgnoringKeyguard && r != starting) {
- // Also ignore invisible activities that are not the currently starting
- // activity (about to be visible).
- continue;
- }
-
- if (r.occludesParent() || r.hasWallpaper) {
- // Stack isn't translucent if it has at least one fullscreen activity
- // that is visible.
- return false;
- }
- }
+ private static boolean isOpaqueActivity(ActivityRecord r, ActivityRecord starting) {
+ if (r.finishing) {
+ // We don't factor in finishing activities when determining translucency since
+ // they will be gone soon.
+ return false;
}
- return true;
+
+ if (!r.visibleIgnoringKeyguard && r != starting) {
+ // Also ignore invisible activities that are not the currently starting
+ // activity (about to be visible).
+ return false;
+ }
+
+ if (r.occludesParent() || r.hasWallpaper) {
+ // Stack isn't translucent if it has at least one fullscreen activity
+ // that is visible.
+ return true;
+ }
+ return false;
}
boolean isTopStackOnDisplay() {
@@ -1958,20 +1951,6 @@
: STACK_VISIBILITY_VISIBLE;
}
- final int rankTaskLayers(int baseLayer) {
- int layer = 0;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- ActivityRecord r = task.topRunningActivityLocked();
- if (r == null || r.finishing || !r.mVisibleRequested) {
- task.mLayerRank = -1;
- } else {
- task.mLayerRank = baseLayer + layer++;
- }
- }
- return layer;
- }
-
/**
* Make sure that all activities that need to be visible in the stack (that is, they
* currently can be seen by the user) actually are and update their configuration.
@@ -2006,12 +1985,6 @@
}
}
- void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- getChildAt(taskNdx).addStartingWindowsForVisibleActivities(taskSwitch);
- }
- }
-
/**
* @return true if the top visible activity wants to occlude the Keyguard, false otherwise
*/
@@ -2127,18 +2100,6 @@
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
- void clearOtherAppTimeTrackers(AppTimeTracker except) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if ( r.appTimeTracker != except) {
- r.appTimeTracker = null;
- }
- }
- }
- }
-
/**
* Called as activities below the top translucent activity are redrawn. When the last one is
* redrawn notify the top activity by calling
@@ -2186,50 +2147,8 @@
*/
boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
Consumer<ActivityRecord> handleBehindFullscreenActivity) {
- boolean aboveTop = true;
- boolean behindFullscreenActivity = false;
-
- if (!shouldBeVisible(null)) {
- // The stack is not visible, so no activity in it should be displaying a starting
- // window. Mark all activities below top and behind fullscreen.
- aboveTop = false;
- behindFullscreenActivity = true;
- }
-
- final boolean handlingOccluded = toCheck == null && handleBehindFullscreenActivity != null;
- if (!handlingOccluded && behindFullscreenActivity) {
- return true;
- }
-
- final ActivityRecord topActivity = topRunningActivityLocked();
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (aboveTop) {
- if (r == topActivity) {
- if (r == toCheck) {
- // It is the top activity in a visible stack.
- return false;
- }
- aboveTop = false;
- }
- behindFullscreenActivity |= r.occludesParent();
- continue;
- }
-
- if (handlingOccluded) {
- handleBehindFullscreenActivity.accept(r);
- } else if (r == toCheck) {
- return behindFullscreenActivity;
- } else if (behindFullscreenActivity) {
- // It is occluded before {@param toCheck} is found.
- return true;
- }
- behindFullscreenActivity |= r.occludesParent();
- }
- }
- return behindFullscreenActivity;
+ return mCheckBehindFullscreenActivityHelper.process(
+ toCheck, handleBehindFullscreenActivity);
}
/**
@@ -2787,7 +2706,7 @@
}
break;
} else if (!isOccluded) {
- isOccluded = task.forAllActivities(ActivityRecord::occludesParent);
+ isOccluded = task.getActivity(ActivityRecord::occludesParent) != null;
}
}
}
@@ -2812,7 +2731,7 @@
// The transition animation and starting window are not needed if {@code allowMoveToFront}
// is false, because the activity won't be visible.
- if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) {
+ if ((!isHomeOrRecentsStack() || hasActivity()) && allowMoveToFront) {
final DisplayContent dc = getDisplay().mDisplayContent;
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
"Prepare open transition: starting " + r);
@@ -2915,8 +2834,7 @@
return true;
}
- private boolean isTaskSwitch(ActivityRecord r,
- ActivityRecord topFocusedActivity) {
+ private boolean isTaskSwitch(ActivityRecord r, ActivityRecord topFocusedActivity) {
return topFocusedActivity != null && r.getTask() != topFocusedActivity.getTask();
}
@@ -2996,23 +2914,6 @@
return stack;
}
- /** Finish all activities that were started for result from the specified activity. */
- final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.resultTo == self && r.requestCode == requestCode) {
- if ((r.resultWho == null && resultWho == null) ||
- (r.resultWho != null && r.resultWho.equals(resultWho))) {
- r.finishIfPossible("request-sub", false /* oomAdj */);
- }
- }
- }
- }
- mService.updateOomAdj();
- }
-
/**
* Finish the topmost activity that belongs to the crashed app. We may also finish the activity
* that requested launch of the crashed one to prevent launch-crash loop.
@@ -3023,103 +2924,84 @@
*/
final Task finishTopCrashedActivityLocked(WindowProcessController app, String reason) {
ActivityRecord r = topRunningActivityLocked();
- Task finishedTask = null;
if (r == null || r.app != app) {
return null;
}
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- finishedTask = r.getTask();
- int taskNdx = mChildren.indexOf(finishedTask);
- final Task task = finishedTask;
- int activityNdx = task.mChildren.indexOf(r);
+ Task finishedTask = r.getTask();
getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
r.finishIfPossible(reason, false /* oomAdj */);
- finishedTask = task;
- // Also terminate any activities below it that aren't yet
- // stopped, to avoid a situation where one will get
- // re-start our crashing activity once it gets resumed again.
- --activityNdx;
- if (activityNdx < 0) {
- do {
- --taskNdx;
- if (taskNdx < 0) {
- break;
- }
- activityNdx = getChildAt(taskNdx).getChildCount() - 1;
- } while (activityNdx < 0);
- }
- if (activityNdx >= 0) {
- r = getChildAt(taskNdx).getChildAt(activityNdx);
- if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
- if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
+
+ // Also terminate any activities below it that aren't yet stopped, to avoid a situation
+ // where one will get re-start our crashing activity once it gets resumed again.
+ final ActivityRecord activityBelow = getActivityBelow(r);
+ if (activityBelow != null) {
+ if (activityBelow.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
+ if (!activityBelow.isActivityTypeHome()
+ || mService.mHomeProcess != activityBelow.app) {
Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- r.finishIfPossible(reason, false /* oomAdj */);
+ + activityBelow.intent.getComponent().flattenToShortString());
+ activityBelow.finishIfPossible(reason, false /* oomAdj */);
}
}
}
+
return finishedTask;
}
- final void finishVoiceTask(IVoiceInteractionSession session) {
- IBinder sessionBinder = session.asBinder();
- boolean didOne = false;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- Task tr = getChildAt(taskNdx);
- if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
- for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.getChildAt(activityNdx);
- if (!r.finishing) {
- r.finishIfPossible("finish-voice", false /* oomAdj */);
- didOne = true;
- }
- }
- } else {
- // Check if any of the activities are using voice
- for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.getChildAt(activityNdx);
- if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
- // Inform of cancellation
- r.clearVoiceSessionLocked();
- try {
- r.app.getThread().scheduleLocalVoiceInteractionStarted(
- r.appToken, null);
- } catch (RemoteException re) {
- // Ok
- }
- mService.finishRunningVoiceLocked();
- break;
- }
- }
- }
- }
+ void finishVoiceTask(IVoiceInteractionSession session) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::finishIfVoiceTask,
+ PooledLambda.__(Task.class), session.asBinder());
+ forAllTasks(c);
+ c.recycle();
+ }
- if (didOne) {
- mService.updateOomAdj();
+ private static void finishIfVoiceTask(Task tr, IBinder binder) {
+ if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
+ tr.forAllActivities((r) -> {
+ if (r.finishing) return;
+ r.finishIfPossible("finish-voice", false /* oomAdj */);
+ tr.mAtmService.updateOomAdj();
+ });
+ } else {
+ // Check if any of the activities are using voice
+ final PooledFunction f = PooledLambda.obtainFunction(
+ ActivityStack::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
+ binder);
+ tr.forAllActivities(f);
+ f.recycle();
}
}
+ private static boolean finishIfVoiceActivity(ActivityRecord r, IBinder binder) {
+ if (r.voiceSession == null || r.voiceSession.asBinder() != binder) return false;
+ // Inform of cancellation
+ r.clearVoiceSessionLocked();
+ try {
+ r.app.getThread().scheduleLocalVoiceInteractionStarted(r.appToken, null);
+ } catch (RemoteException re) {
+ // Ok Boomer...
+ }
+ r.mAtmService.finishRunningVoiceLocked();
+ return true;
+ }
+
/** Finish all activities in the stack without waiting. */
void finishAllActivitiesImmediately() {
- boolean noActivitiesInStack = true;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- noActivitiesInStack = false;
- Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
- r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
- }
- }
- if (noActivitiesInStack) {
+ if (!hasChild()) {
removeIfPossible();
+ return;
}
+ forAllActivities((r) -> {
+ Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
+ r.destroyIfPossible("finishAllActivitiesImmediately");
+ });
}
/** @return true if the stack behind this one is a standard activity type. */
- boolean inFrontOfStandardStack() {
+ private boolean inFrontOfStandardStack() {
final ActivityDisplay display = getDisplay();
if (display == null) {
return false;
@@ -3166,28 +3048,25 @@
return false;
}
- final boolean navigateUpToLocked(ActivityRecord srec, Intent destIntent, int resultCode,
+ boolean navigateUpTo(ActivityRecord srec, Intent destIntent, int resultCode,
Intent resultData) {
final Task task = srec.getTask();
- final ArrayList<ActivityRecord> activities = task.mChildren;
- final int start = activities.indexOf(srec);
- if (!mChildren.contains(task) || (start < 0)) {
+
+ if (!mChildren.contains(task) || !task.hasChild(srec)) {
return false;
}
- int finishTo = start - 1;
- ActivityRecord parent = finishTo < 0 ? null : task.getChildAt(finishTo);
+
+ ActivityRecord parent = task.getActivityBelow(srec);
boolean foundParentInTask = false;
final ComponentName dest = destIntent.getComponent();
- if (start > 0 && dest != null) {
- for (int i = finishTo; i >= 0; i--) {
- ActivityRecord r = task.getChildAt(i);
- if (r.info.packageName.equals(dest.getPackageName()) &&
- r.info.name.equals(dest.getClassName())) {
- finishTo = i;
- parent = r;
- foundParentInTask = true;
- break;
- }
+ if (task.getBottomMostActivity() != srec && dest != null) {
+ final ActivityRecord candidate = task.getActivity((ar) ->
+ ar.info.packageName.equals(dest.getPackageName()) &&
+ ar.info.name.equals(dest.getClassName()), srec, false /*includeBoundary*/,
+ true /*traverseTopToBottom*/);
+ if (candidate != null) {
+ parent = candidate;
+ foundParentInTask = true;
}
}
@@ -3212,13 +3091,24 @@
}
}
final long origId = Binder.clearCallingIdentity();
- for (int i = start; i > finishTo; i--) {
- final ActivityRecord r = activities.get(i);
- r.finishIfPossible(resultCode, resultData, "navigate-up", true /* oomAdj */);
+
+ final int[] resultCodeHolder = new int[1];
+ resultCodeHolder[0] = resultCode;
+ final Intent[] resultDataHolder = new Intent[1];
+ resultDataHolder[0] = resultData;
+ final ActivityRecord finalParent = parent;
+ task.forAllActivities((ar) -> {
+ if (ar == finalParent) return true;
+
+ ar.finishIfPossible(
+ resultCodeHolder[0], resultDataHolder[0], "navigate-up", true /* oomAdj */);
// Only return the supplied result for the first activity finished
- resultCode = Activity.RESULT_CANCELED;
- resultData = null;
- }
+ resultCodeHolder[0] = Activity.RESULT_CANCELED;
+ resultDataHolder[0] = null;
+ return false;
+ }, srec, true, true);
+ resultCode = resultCodeHolder[0];
+ resultData = resultDataHolder[0];
if (parent != null && foundParentInTask) {
final int parentLaunchMode = parent.info.launchMode;
@@ -3285,7 +3175,6 @@
}
}
- /// HANDLER INTERFACE BEGIN
void removeTimeoutsForActivity(ActivityRecord r) {
mStackSupervisor.removeTimeoutsForActivityLocked(r);
removePauseTimeoutForActivity(r);
@@ -3343,86 +3232,33 @@
}
/// HANDLER INTERFACE END
- private void destroyActivitiesLocked(WindowProcessController owner, String reason) {
- boolean lastIsOpaque = false;
- boolean activityRemoved = false;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.finishing) {
- continue;
- }
- if (r.occludesParent()) {
- lastIsOpaque = true;
- }
- if (owner != null && r.app != owner) {
- continue;
- }
- if (!lastIsOpaque) {
- continue;
- }
- if (r.isDestroyable()) {
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
- + " in state " + r.getState()
- + " resumed=" + mResumedActivity
- + " pausing=" + mPausingActivity + " for reason " + reason);
- if (r.destroyImmediately(true /* removeFromTask */, reason)) {
- activityRemoved = true;
- }
- }
- }
- }
- if (activityRemoved) {
+ private void destroyActivities(WindowProcessController owner, String reason) {
+ try {
+ mStackSupervisor.beginDeferResume();
+
+ final PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::destroyActivity,
+ PooledLambda.__(ActivityRecord.class), owner, reason);
+ forAllActivities(c);
+ c.recycle();
+ } finally {
+ mStackSupervisor.endDeferResume();
mRootActivityContainer.resumeFocusedStacksTopActivities();
}
}
- final int releaseSomeActivitiesLocked(WindowProcessController app, ArraySet<Task> tasks,
- String reason) {
- // Iterate over tasks starting at the back (oldest) first.
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + app);
- int maxTasks = tasks.size() / 4;
- if (maxTasks < 1) {
- maxTasks = 1;
- }
- int numReleased = 0;
- for (int taskNdx = 0; taskNdx < getChildCount() && maxTasks > 0; taskNdx++) {
- final Task task = getChildAt(taskNdx);
- if (!tasks.contains(task)) {
- continue;
- }
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
- int curNum = 0;
- for (int actNdx = 0; actNdx < task.getChildCount(); actNdx++) {
- final ActivityRecord activity = task.getChildAt(actNdx);
- if (activity.app == app && activity.isDestroyable()) {
- if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
- + " in state " + activity.getState() + " resumed=" + mResumedActivity
- + " pausing=" + mPausingActivity + " for reason " + reason);
- activity.destroyImmediately(true /* removeFromApp */, reason);
- if (task.getChildAt(actNdx) != activity) {
- // Was removed from list, back up so we don't miss the next one.
- actNdx--;
- }
- curNum++;
- }
- }
- if (curNum > 0) {
- numReleased += curNum;
- maxTasks--;
- if (getChildAt(taskNdx) != task) {
- // The entire task got removed, back up so we don't miss the next one.
- taskNdx--;
- }
- }
- }
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE,
- "Done releasing: did " + numReleased + " activities");
- return numReleased;
+ private static void destroyActivity(
+ ActivityRecord r, WindowProcessController owner, String reason) {
+ if (r.finishing || (owner != null && r.app != owner) || !r.isDestroyable()) return;
+
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r
+ + " in state " + r.getState()
+ + " resumed=" + r.getStack().mResumedActivity
+ + " pausing=" + r.getStack().mPausingActivity + " for reason " + reason);
+
+ r.destroyImmediately(true /* removeFromTask */, reason);
}
- private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
+ private void removeHistoryRecordsForApp(ArrayList<ActivityRecord> list,
WindowProcessController app, String listName) {
int i = list.size();
if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
@@ -3439,107 +3275,15 @@
}
}
- private boolean removeHistoryRecordsForAppLocked(WindowProcessController app) {
- removeHistoryRecordsForAppLocked(mLruActivities, app, "mLruActivities");
- removeHistoryRecordsForAppLocked(mStackSupervisor.mStoppingActivities, app,
+ private boolean removeHistoryRecordsForApp(WindowProcessController app) {
+ removeHistoryRecordsForApp(mLruActivities, app, "mLruActivities");
+ removeHistoryRecordsForApp(mStackSupervisor.mStoppingActivities, app,
"mStoppingActivities");
- removeHistoryRecordsForAppLocked(mStackSupervisor.mGoingToSleepActivities, app,
+ removeHistoryRecordsForApp(mStackSupervisor.mGoingToSleepActivities, app,
"mGoingToSleepActivities");
- removeHistoryRecordsForAppLocked(mStackSupervisor.mFinishingActivities, app,
+ removeHistoryRecordsForApp(mStackSupervisor.mFinishingActivities, app,
"mFinishingActivities");
-
- final boolean isProcessRemoved = app.isRemoved();
- if (isProcessRemoved) {
- // The package of the died process should be force-stopped, so make its activities as
- // finishing to prevent the process from being started again if the next top (or being
- // visible) activity also resides in the same process.
- app.makeFinishingForProcessRemoved();
- }
-
- boolean hasVisibleActivities = false;
-
- // Clean out the history list.
- int i = numActivities();
- if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
- "Removing app " + app + " from history with " + i + " entries");
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = getChildAt(taskNdx).mChildren;
- mTmpActivities.clear();
- mTmpActivities.addAll(activities);
-
- while (!mTmpActivities.isEmpty()) {
- final int targetIndex = mTmpActivities.size() - 1;
- final ActivityRecord r = mTmpActivities.remove(targetIndex);
- if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
- "Record #" + targetIndex + " " + r + ": app=" + r.app);
-
- if (r.app == app) {
- if (r.isVisible() || r.mVisibleRequested) {
- // While an activity launches a new activity, it's possible that the old
- // activity is already requested to be hidden (mVisibleRequested=false), but
- // this visibility is not yet committed, so isVisible()=true.
- hasVisibleActivities = true;
- }
- final boolean remove;
- if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
- || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
- && r.launchCount < 3 && !r.finishing) {
- // If the process crashed during a resize, always try to relaunch it, unless
- // it has failed more than twice. Skip activities that's already finishing
- // cleanly by itself.
- remove = false;
- } else if ((!r.hasSavedState() && !r.stateNotNeeded
- && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) {
- // Don't currently have state for the activity, or
- // it is finishing -- always remove it.
- remove = true;
- } else if (!r.mVisibleRequested && r.launchCount > 2
- && r.lastLaunchTime > (SystemClock.uptimeMillis() - 60000)) {
- // We have launched this activity too many times since it was
- // able to run, so give up and remove it.
- // (Note if the activity is visible, we don't remove the record.
- // We leave the dead window on the screen but the process will
- // not be restarted unless user explicitly tap on it.)
- remove = true;
- } else {
- // The process may be gone, but the activity lives on!
- remove = false;
- }
- if (remove) {
- if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE,
- "Removing activity " + r + " from stack at " + i
- + ": hasSavedState=" + r.hasSavedState()
- + " stateNotNeeded=" + r.stateNotNeeded
- + " finishing=" + r.finishing
- + " state=" + r.getState() + " callers=" + Debug.getCallers(5));
- if (!r.finishing || isProcessRemoved) {
- Slog.w(TAG, "Force removing " + r + ": app died, no saved state");
- EventLogTags.writeWmFinishActivity(r.mUserId,
- System.identityHashCode(r), r.getTask().mTaskId,
- r.shortComponentName, "proc died without state saved");
- }
- } else {
- // We have the current state for this activity, so
- // it can be restarted later when needed.
- if (DEBUG_ALL) Slog.v(TAG, "Keeping entry, setting app to null");
- if (DEBUG_APP) Slog.v(TAG_APP,
- "Clearing app during removeHistory for activity " + r);
- r.app = null;
- // Set nowVisible to previous visible state. If the app was visible while
- // it died, we leave the dead window on screen so it's basically visible.
- // This is needed when user later tap on the dead window, we need to stop
- // other apps when user transfers focus to the restarted activity.
- r.nowVisible = r.mVisibleRequested;
- }
- r.cleanUp(true /* cleanServices */, true /* setState */);
- if (remove) {
- r.removeFromHistory("appDied");
- }
- }
- }
- }
-
- return hasVisibleActivities;
+ return mRemoveHistoryRecordsForApp.process(app);
}
private void updateTransitLocked(int transit, ActivityOptions options) {
@@ -3559,8 +3303,7 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final ActivityStack topStack = getDisplay().getTopStack();
- final ActivityRecord topActivity = topStack != null
- ? topStack.getTopNonFinishingActivity() : null;
+ final ActivityRecord topActivity = topStack != null ? topStack.getTopNonFinishingActivity() : null;
final int numTasks = getChildCount();
final int index = mChildren.indexOf(tr);
if (numTasks == 0 || index < 0) {
@@ -3575,9 +3318,10 @@
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
- for (int i = tr.getChildCount() - 1; i >= 0; i--) {
- tr.getChildAt(i).appTimeTracker = timeTracker;
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
+ PooledLambda.__(ActivityRecord.class), timeTracker);
+ tr.forAllActivities(c);
+ c.recycle();
}
try {
@@ -3711,38 +3455,8 @@
/**
* Ensures all visible activities at or below the input activity have the right configuration.
*/
- void ensureVisibleActivitiesConfigurationLocked(ActivityRecord start, boolean preserveWindow) {
- if (start == null || !start.mVisibleRequested) {
- return;
- }
-
- final Task startTask = start.getTask();
- boolean behindFullscreen = false;
- boolean updatedConfig = false;
-
- for (int taskIndex = mChildren.indexOf(startTask); taskIndex >= 0; --taskIndex) {
- final Task task = getChildAt(taskIndex);
- final ArrayList<ActivityRecord> activities = task.mChildren;
- int activityIndex = (start.getTask() == task)
- ? activities.indexOf(start) : activities.size() - 1;
- for (; activityIndex >= 0; --activityIndex) {
- final ActivityRecord r = activities.get(activityIndex);
- updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
- preserveWindow);
- if (r.occludesParent()) {
- behindFullscreen = true;
- break;
- }
- }
- if (behindFullscreen) {
- break;
- }
- }
- if (updatedConfig) {
- // Ensure the resumed state of the focus activity if we updated the configuration of
- // any activity.
- mRootActivityContainer.resumeFocusedStacksTopActivities();
- }
+ void ensureVisibleActivitiesConfiguration(ActivityRecord start, boolean preserveWindow) {
+ mEnsureVisibleActivitiesConfigHelper.process(start, preserveWindow);
}
// TODO: Can only be called from special methods in ActivityStackSupervisor.
@@ -3774,7 +3488,7 @@
setBounds(bounds);
if (!deferResume) {
- ensureVisibleActivitiesConfigurationLocked(
+ ensureVisibleActivitiesConfiguration(
topRunningActivityLocked(), preserveWindows);
}
} finally {
@@ -3818,85 +3532,23 @@
}
}
- boolean willActivityBeVisibleLocked(IBinder token) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.appToken == token) {
- return true;
- }
- if (r.occludesParent() && !r.finishing) {
- return false;
- }
- }
- }
+ boolean willActivityBeVisible(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
return false;
}
- if (r.finishing) Slog.e(TAG, "willActivityBeVisibleLocked: Returning false,"
+
+ // See if there is an occluding activity on-top of this one.
+ final ActivityRecord occludingActivity = getActivity((ar) ->
+ ar.occludesParent() && !ar.finishing,
+ r, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ if (occludingActivity != null) return false;
+
+ if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
+ " would have returned true for r=" + r);
return !r.finishing;
}
- void closeSystemDialogsLocked() {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- r.finishIfPossible("close-sys", true /* oomAdj */);
- }
- }
- }
- }
-
- boolean finishDisabledPackageActivitiesLocked(String packageName, Set<String> filterByClasses,
- boolean doit, boolean evenPersistent, int userId) {
- boolean didSomething = false;
- Task lastTask = null;
- ComponentName homeActivity = null;
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = getChildAt(taskNdx).mChildren;
- mTmpActivities.clear();
- mTmpActivities.addAll(activities);
-
- while (!mTmpActivities.isEmpty()) {
- ActivityRecord r = mTmpActivities.remove(0);
- final boolean sameComponent =
- (r.packageName.equals(packageName) && (filterByClasses == null
- || filterByClasses.contains(r.mActivityComponent.getClassName())))
- || (packageName == null && r.mUserId == userId);
- if ((userId == UserHandle.USER_ALL || r.mUserId == userId)
- && (sameComponent || r.getTask() == lastTask)
- && (r.app == null || evenPersistent || !r.app.isPersistent())) {
- if (!doit) {
- if (r.finishing) {
- // If this activity is just finishing, then it is not
- // interesting as far as something to stop.
- continue;
- }
- return true;
- }
- if (r.isActivityTypeHome()) {
- if (homeActivity != null && homeActivity.equals(r.mActivityComponent)) {
- Slog.i(TAG, "Skip force-stop again " + r);
- continue;
- } else {
- homeActivity = r.mActivityComponent;
- }
- }
- didSomething = true;
- Slog.i(TAG, " Force finishing activity " + r);
- lastTask = r.getTask();
- r.finishIfPossible("force-stop", true);
- }
- }
- }
- return didSomething;
- }
-
/**
* @return The set of running tasks through {@param tasksOut} that are available to the caller.
* If {@param ignoreActivityType} or {@param ignoreWindowingMode} are not undefined,
@@ -3948,14 +3600,11 @@
}
void unhandledBackLocked() {
- final int top = getChildCount() - 1;
- if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
- if (top >= 0) {
- final Task task = getChildAt(top);
- int activityTop = task.getChildCount() - 1;
- if (activityTop >= 0) {
- task.getChildAt(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
- }
+ final ActivityRecord topActivity = getTopMostActivity();
+ if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
+ "Performing unhandledBack(): top activity: " + topActivity);
+ if (topActivity != null) {
+ topActivity.finishIfPossible("unhandled-back", true /* oomAdj */);
}
}
@@ -3975,25 +3624,7 @@
mLastNoHistoryActivity = null;
}
- return removeHistoryRecordsForAppLocked(app);
- }
-
- void handleAppCrash(WindowProcessController app) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = task.getChildAt(activityNdx);
- if (r.app == app) {
- Slog.w(TAG, " Force finishing activity "
- + r.intent.getComponent().flattenToShortString());
- // Force the destroy to skip right to removal.
- r.app = null;
- getDisplay().mDisplayContent.prepareAppTransition(
- TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- r.destroyIfPossible("handleAppCrashedLocked");
- }
- }
- }
+ return removeHistoryRecordsForApp(app);
}
boolean dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll, boolean dumpClient,
@@ -4004,8 +3635,7 @@
pw.println(" isSleeping=" + shouldSleepActivities());
pw.println(" mBounds=" + getRequestedOverrideBounds());
- boolean printed = dumpActivitiesLocked(fd, pw, dumpAll, dumpClient, dumpPackage,
- needSep);
+ boolean printed = dumpActivities(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
printed |= dumpHistoryList(fd, pw, mLruActivities, " ", "Run", false,
!dumpAll, false, dumpPackage, true,
@@ -4037,15 +3667,14 @@
return printed;
}
- boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
+ private boolean dumpActivities(FileDescriptor fd, PrintWriter pw, boolean dumpAll,
boolean dumpClient, String dumpPackage, boolean needSep) {
if (!hasChild()) {
return false;
}
final String prefix = " ";
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
+ forAllTasks((task) -> {
if (needSep) {
pw.println("");
}
@@ -4056,9 +3685,11 @@
pw.println(prefix + "mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
pw.println(prefix + "* " + task);
task.dump(pw, prefix + " ");
- dumpHistoryList(fd, pw, getChildAt(taskNdx).mChildren,
- prefix, "Hist", true, !dumpAll, dumpClient, dumpPackage, false, null, task);
- }
+ final ArrayList<ActivityRecord> activities = new ArrayList<>();
+ forAllActivities((Consumer<ActivityRecord>) activities::add);
+ dumpHistoryList(fd, pw, activities, prefix, "Hist", true, !dumpAll, dumpClient,
+ dumpPackage, false, null, task);
+ });
return true;
}
@@ -4066,31 +3697,21 @@
ArrayList<ActivityRecord> activities = new ArrayList<>();
if ("all".equals(name)) {
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- activities.addAll(getChildAt(taskNdx).mChildren);
- }
+ forAllActivities((Consumer<ActivityRecord>) activities::add);
} else if ("top".equals(name)) {
- final int top = getChildCount() - 1;
- if (top >= 0) {
- final Task task = getChildAt(top);
- int listTop = task.getChildCount() - 1;
- if (listTop >= 0) {
- activities.add(task.getChildAt(listTop));
- }
+ final ActivityRecord topActivity = getTopMostActivity();
+ if (topActivity != null) {
+ activities.add(topActivity);
}
} else {
ItemMatcher matcher = new ItemMatcher();
matcher.build(name);
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r1 = task.getChildAt(activityNdx);
- if (matcher.match(r1, r1.intent.getComponent())) {
- activities.add(r1);
- }
+ forAllActivities((r) -> {
+ if (matcher.match(r, r.intent.getComponent())) {
+ activities.add(r);
}
- }
+ });
}
return activities;
@@ -4101,22 +3722,24 @@
// All activities that came from the package must be
// restarted as if there was a config change.
- for (int taskNdx = getChildCount() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = getChildAt(taskNdx);
- for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord a = task.getChildAt(activityNdx);
- if (a.info.packageName.equals(packageName)) {
- a.forceNewConfig = true;
- if (starting != null && a == starting && a.mVisibleRequested) {
- a.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
- }
- }
- }
- }
+ PooledConsumer c = PooledLambda.obtainConsumer(ActivityStack::restartPackage,
+ PooledLambda.__(ActivityRecord.class), starting, packageName);
+ forAllActivities(c);
+ c.recycle();
return starting;
}
+ private static void restartPackage(
+ ActivityRecord r, ActivityRecord starting, String packageName) {
+ if (r.info.packageName.equals(packageName)) {
+ r.forceNewConfig = true;
+ if (starting != null && r == starting && r.mVisibleRequested) {
+ r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
+ }
+ }
+ }
+
/**
* Removes the input task from this stack.
*
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d088a5e..0376d2c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -138,6 +138,7 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.UserState;
@@ -825,7 +826,7 @@
task.mTaskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
- updateHomeProcess(task.getChildAt(0).app);
+ updateHomeProcess(task.getBottomMostActivity().app);
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1688,7 +1689,7 @@
}
}
if (!deferResume) {
- stack.ensureVisibleActivitiesConfigurationLocked(r, preserveWindows);
+ stack.ensureVisibleActivitiesConfiguration(r, preserveWindows);
}
} finally {
mAllowDockedStackResize = true;
@@ -2485,18 +2486,23 @@
return;
}
- for (int i = task.getChildCount() - 1; i >= 0; i--) {
- final ActivityRecord r = task.getChildAt(i);
- if (r.attachedToProcess()) {
- mMultiWindowModeChangedActivities.add(r);
- }
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityStackSupervisor::addToMultiWindowModeChangedList, this,
+ PooledLambda.__(ActivityRecord.class));
+ task.forAllActivities(c);
+ c.recycle();
if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
}
}
+ private void addToMultiWindowModeChangedList(ActivityRecord r) {
+ if (r.attachedToProcess()) {
+ mMultiWindowModeChangedActivities.add(r);
+ }
+ }
+
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, ActivityStack prevStack) {
final ActivityStack stack = task.getStack();
if (prevStack == null || prevStack == stack
@@ -2507,17 +2513,13 @@
scheduleUpdatePictureInPictureModeIfNeeded(task, stack.getRequestedOverrideBounds());
}
- void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetStackBounds) {
- for (int i = task.getChildCount() - 1; i >= 0; i--) {
- final ActivityRecord r = task.getChildAt(i);
- if (r.attachedToProcess()) {
- mPipModeChangedActivities.add(r);
- // If we are scheduling pip change, then remove this activity from multi-window
- // change list as the processing of pip change will make sure multi-window changed
- // message is processed in the right order relative to pip changed.
- mMultiWindowModeChangedActivities.remove(r);
- }
- }
+ private void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetStackBounds) {
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityStackSupervisor::addToPipModeChangedList, this,
+ PooledLambda.__(ActivityRecord.class));
+ task.forAllActivities(c);
+ c.recycle();
+
mPipModeChangedTargetStackBounds = targetStackBounds;
if (!mHandler.hasMessages(REPORT_PIP_MODE_CHANGED_MSG)) {
@@ -2525,14 +2527,23 @@
}
}
+ private void addToPipModeChangedList(ActivityRecord r) {
+ if (!r.attachedToProcess()) return;
+
+ mPipModeChangedActivities.add(r);
+ // If we are scheduling pip change, then remove this activity from multi-window
+ // change list as the processing of pip change will make sure multi-window changed
+ // message is processed in the right order relative to pip changed.
+ mMultiWindowModeChangedActivities.remove(r);
+ }
+
void updatePictureInPictureMode(Task task, Rect targetStackBounds, boolean forceUpdate) {
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
- for (int i = task.getChildCount() - 1; i >= 0; i--) {
- final ActivityRecord r = task.getChildAt(i);
- if (r.attachedToProcess()) {
- r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
- }
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityRecord::updatePictureInPictureMode,
+ PooledLambda.__(ActivityRecord.class), targetStackBounds, forceUpdate);
+ task.forAllActivities(c);
+ c.recycle();
}
void wakeUp(String reason) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 8420695..b2fb93d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -156,16 +156,16 @@
mInTask = inTask;
mActivityOptions = activityOptions;
- if (interceptSuspendedPackageIfNeeded()) {
- // Skip the rest of interceptions as the package is suspended by device admin so
- // no user action can undo this.
- return true;
- }
if (interceptQuietProfileIfNeeded()) {
// If work profile is turned off, skip the work challenge since the profile can only
// be unlocked when profile's user is running.
return true;
}
+ if (interceptSuspendedPackageIfNeeded()) {
+ // Skip the rest of interceptions as the package is suspended by device admin so
+ // no user action can undo this.
+ return true;
+ }
if (interceptHarmfulAppIfNeeded()) {
// If the app has a "harmful app" warning associated with it, we should ask to uninstall
// before issuing the work challenge.
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 0f912f1..a496396 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -149,7 +149,8 @@
private final ActivityStartController mController;
// Share state variable among methods when starting an activity.
- private ActivityRecord mStartActivity;
+ @VisibleForTesting
+ ActivityRecord mStartActivity;
private Intent mIntent;
private int mCallingUid;
private ActivityOptions mOptions;
@@ -173,7 +174,8 @@
private int mPreferredDisplayId;
private Task mInTask;
- private boolean mAddingToTask;
+ @VisibleForTesting
+ boolean mAddingToTask;
private Task mReuseTask;
private ActivityInfo mNewTaskInfo;
@@ -1444,7 +1446,7 @@
// Stack should also be detached from display and be removed if it's empty.
if (startedActivityStack != null && startedActivityStack.isAttached()
- && startedActivityStack.numActivities() == 0
+ && !startedActivityStack.hasActivity()
&& !startedActivityStack.isActivityTypeHome()) {
startedActivityStack.removeIfPossible();
startedActivityStack = null;
@@ -1638,7 +1640,7 @@
return START_CANCELED;
}
- if (mRestrictedBgActivity && (newTask || !targetTask.containsAppUid(mCallingUid))
+ if (mRestrictedBgActivity && (newTask || !targetTask.isUidPresent(mCallingUid))
&& handleBackgroundActivityAbort(mStartActivity)) {
Slog.e(TAG, "Abort background activity starts from " + mCallingUid);
return START_ABORTED;
@@ -1665,7 +1667,16 @@
* - Comply to the specified activity launch flags
* - Determine whether need to add a new activity on top or just brought the task to front.
*/
- private int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask) {
+ @VisibleForTesting
+ int recycleTask(Task targetTask, ActivityRecord targetTaskTop, Task reusedTask) {
+ // Should not recycle task which is from a different user, just adding the starting
+ // activity to the task.
+ if (targetTask.mUserId != mStartActivity.mUserId) {
+ mTargetStack = targetTask.getStack();
+ mAddingToTask = true;
+ return START_SUCCESS;
+ }
+
// True if we are clearing top and resetting of a standard (default) launch mode
// ({@code LAUNCH_MULTIPLE}) activity. The existing activity will be finished.
final boolean clearTopAndResetStandardLaunchMode =
@@ -1869,8 +1880,8 @@
// In this case, we are launching an activity in our own task that may
// already be running somewhere in the history, and we want to shuffle it to
// the front of the stack if so.
- final ActivityRecord act = targetTask.findActivityInHistoryLocked(
- mStartActivity);
+ final ActivityRecord act =
+ targetTask.findActivityInHistory(mStartActivity.mActivityComponent);
if (act != null) {
final Task task = act.getTask();
task.moveActivityToFrontLocked(act);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bcd5d36..df03940 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -247,6 +247,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
@@ -695,6 +697,13 @@
int caller() default NONE;
}
+ private final Runnable mUpdateOomAdjRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mAmInternal.updateOomAdj();
+ }
+ };
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public ActivityTaskManagerService(Context context) {
mContext = context;
@@ -1661,7 +1670,14 @@
if (getLockTaskController().activityBlockedFromFinish(r)) {
return false;
}
- r.finishActivityAffinity();
+
+ final PooledFunction p = PooledLambda.obtainFunction(
+ ActivityRecord::finishIfSameAffinity, r,
+ PooledLambda.__(ActivityRecord.class));
+ r.getTask().forAllActivities(
+ p, r, true /*includeBoundary*/, true /*traverseTopToBottom*/);
+ p.recycle();
+
return true;
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1994,10 +2010,8 @@
if (r == null) {
return false;
}
- final Task task = r.getTask();
- int index = task.mChildren.lastIndexOf(r);
- if (index > 0) {
- ActivityRecord under = task.getChildAt(index - 1);
+ final ActivityRecord under = r.getTask().getActivityBelow(r);
+ if (under != null) {
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
return r.setOccludesParent(false);
@@ -2163,7 +2177,7 @@
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
- return r.getActivityStack().navigateUpToLocked(
+ return r.getActivityStack().navigateUpTo(
r, destIntent, resultCode, resultData);
}
return false;
@@ -2548,11 +2562,22 @@
public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
- ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
- r.getActivityStack().finishSubActivityLocked(r, resultWho, requestCode);
+ try {
+ ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) return;
+
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityRecord::finishIfSubActivity, PooledLambda.__(ActivityRecord.class),
+ r, resultWho, requestCode);
+ // TODO: This should probably only loop over the task since you need to be in the
+ // same task to return results.
+ r.getActivityStack().forAllActivities(c);
+ c.recycle();
+
+ updateOomAdj();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
- Binder.restoreCallingIdentity(origId);
}
}
@@ -2561,7 +2586,7 @@
synchronized (mGlobalLock) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- return stack.willActivityBeVisibleLocked(token);
+ return stack.willActivityBeVisible(token);
}
return false;
}
@@ -3332,7 +3357,7 @@
final long origId = Binder.clearCallingIdentity();
try {
final WindowProcessController app = getProcessController(appInt);
- mRootActivityContainer.releaseSomeActivitiesLocked(app, "low-mem");
+ app.releaseSomeActivities("low-mem");
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -5545,7 +5570,8 @@
}
void updateOomAdj() {
- mH.post(mAmInternal::updateOomAdj);
+ mH.removeCallbacks(mUpdateOomAdjRunnable);
+ mH.post(mUpdateOomAdjRunnable);
}
void updateCpuStats() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 283b92c..b046b08 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2482,7 +2482,7 @@
&& !mDividerControllerLocked.isImeHideRequested();
final ActivityStack dockedStack = getSplitScreenPrimaryStack();
final boolean dockVisible = dockedStack != null;
- final Task topDockedTask = dockVisible ? dockedStack.getTopChild() : null;
+ final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 3b58eca..7645de5 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -196,8 +196,7 @@
// Some stack visibility might change (e.g. docked stack)
mRootActivityContainer.resumeFocusedStacksTopActivities();
mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mRootActivityContainer.addStartingWindowsForVisibleActivities(
- true /* taskSwitch */);
+ mRootActivityContainer.addStartingWindowsForVisibleActivities();
mWindowManager.executeAppTransition();
} finally {
mService.continueWindowLayout();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 824a3c8..f2e9505 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -42,6 +42,8 @@
import android.util.Slog;
import android.view.IRecentsAnimationRunner;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
@@ -490,13 +492,15 @@
return null;
}
- for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
- final Task task = targetStack.getChildAt(i);
- if (task.mUserId == mUserId
- && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent())) {
- return task.getTopNonFinishingActivity();
- }
- }
- return null;
+ final PooledPredicate p = PooledLambda.obtainPredicate(RecentsAnimation::matchesTarget,
+ this, PooledLambda.__(Task.class));
+ final Task task = targetStack.getTask(p);
+ p.recycle();
+ return task != null ? task.getTopNonFinishingActivity() : null;
+ }
+
+ private boolean matchesTarget(Task task) {
+ return task.mUserId == mUserId
+ && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent());
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 282144e..4c5ca38 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -55,6 +55,9 @@
import android.view.SurfaceControl.Transaction;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.protolog.common.ProtoLog;
@@ -369,13 +372,13 @@
final ActivityStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED,
targetActivityType);
if (targetStack != null) {
- for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
- final Task t = targetStack.getChildAt(i);
- if (!visibleTasks.contains(t)) {
- visibleTasks.add(t);
- }
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
+ { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
+ visibleTasks);
+ targetStack.forAllTasks(c);
+ c.recycle();
}
+
final int taskCount = visibleTasks.size();
for (int i = 0; i < taskCount; i++) {
final Task task = visibleTasks.get(i);
@@ -800,12 +803,12 @@
private boolean isAnimatingApp(ActivityRecord activity) {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
final Task task = mPendingAnimations.get(i).mTask;
- for (int j = task.getChildCount() - 1; j >= 0; j--) {
- final ActivityRecord app = task.getChildAt(j);
- if (app == activity) {
- return true;
- }
- }
+ final PooledFunction f = PooledLambda.obtainFunction(
+ (a, b) -> a == b, activity,
+ PooledLambda.__(ActivityRecord.class));
+ boolean isAnimatingApp = task.forAllActivities(f);
+ f.recycle();
+ return isAnimatingApp;
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 413dfd5..3aa91d5 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -125,8 +125,7 @@
// TODO: We should probably look for other stacks also, since corresponding
// task with the same affinity is unlikely to be in the same stack.
final Task targetTask;
- final ActivityRecord bottom = mParent.getActivity(
- (ar) -> true, false /*traverseTopToBottom*/);
+ final ActivityRecord bottom = mParent.getBottomMostActivity();
if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
// If the activity currently at the bottom has the same task affinity as
@@ -210,10 +209,8 @@
// we have put it on top of another instance of the same activity? Then we drop
// the instance below so it remains singleTop.
if (r.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) {
- final ArrayList<ActivityRecord> taskActivities = mTargetTask.mChildren;
- final int targetNdx = taskActivities.indexOf(r);
- if (targetNdx > 0) {
- final ActivityRecord p = taskActivities.get(targetNdx - 1);
+ final ActivityRecord p = mTargetTask.getActivityBelow(r);
+ if (p != null) {
if (p.intent.getComponent().equals(r.intent.getComponent())) {
p.finishIfPossible("replace", false /* oomAdj */);
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 8f1d6ee..5fd59fa 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -33,6 +33,7 @@
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
import static com.android.server.am.ActivityStackSupervisorProto.CONFIGURATION_CONTAINER;
@@ -86,6 +87,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.power.V1_0.PowerHint;
+import android.net.Uri;
import android.os.FactoryTest;
import android.os.IBinder;
import android.os.RemoteException;
@@ -108,6 +110,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
+import com.android.internal.util.ToBooleanFunction;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
@@ -121,6 +128,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -201,14 +209,46 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
+ private int mTmpTaskLayerRank;
- private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+ private boolean mTmpBoolean;
+ private RemoteException mTmpRemoteException;
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
- static class FindTaskResult {
+ static class FindTaskResult implements ToBooleanFunction<Task> {
ActivityRecord mRecord;
boolean mIdealMatch;
+ private ActivityRecord mTarget;
+ private Intent intent;
+ private ActivityInfo info;
+ private ComponentName cls;
+ private int userId;
+ private boolean isDocument;
+ private Uri documentData;
+
+ /**
+ * Returns the top activity in any existing task matching the given Intent in the input
+ * result. Returns null if no such task is found.
+ */
+ void process(ActivityRecord target, ActivityStack parent) {
+ mTarget = target;
+
+ intent = target.intent;
+ info = target.info;
+ cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+ userId = UserHandle.getUserId(info.applicationInfo.uid);
+ isDocument = intent != null & intent.isDocument();
+ // If documentData is non-null then it must match the existing task data.
+ documentData = isDocument ? intent.getData() : null;
+
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + parent);
+ parent.forAllTasks(this);
+ }
+
void clear() {
mRecord = null;
mIdealMatch = false;
@@ -218,6 +258,84 @@
mRecord = result.mRecord;
mIdealMatch = result.mIdealMatch;
}
+
+ @Override
+ public boolean apply(Task task) {
+ if (task.voiceSession != null) {
+ // We never match voice sessions; those always run independently.
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
+ return false;
+ }
+ if (task.mUserId != userId) {
+ // Looking for a different task.
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
+ return false;
+ }
+
+ // Overlays should not be considered as the task's logical top activity.
+ final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
+ if (r == null || r.finishing || r.mUserId != userId ||
+ r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
+ return false;
+ }
+ if (!r.hasCompatibleActivityType(mTarget)) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch activity type");
+ return false;
+ }
+
+ final Intent taskIntent = task.intent;
+ final Intent affinityIntent = task.affinityIntent;
+ final boolean taskIsDocument;
+ final Uri taskDocumentData;
+ if (taskIntent != null && taskIntent.isDocument()) {
+ taskIsDocument = true;
+ taskDocumentData = taskIntent.getData();
+ } else if (affinityIntent != null && affinityIntent.isDocument()) {
+ taskIsDocument = true;
+ taskDocumentData = affinityIntent.getData();
+ } else {
+ taskIsDocument = false;
+ taskDocumentData = null;
+ }
+
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
+ + (task.realActivity != null ? task.realActivity.flattenToShortString() : "")
+ + "/aff=" + r.getTask().rootAffinity + " to new cls="
+ + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
+ // TODO Refactor to remove duplications. Check if logic can be simplified.
+ if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
+ && Objects.equals(documentData, taskDocumentData)) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+ //dump();
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+ "For Intent " + intent + " bringing to top: " + r.intent);
+ mRecord = r;
+ mIdealMatch = true;
+ return true;
+ } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
+ affinityIntent.getComponent().compareTo(cls) == 0 &&
+ Objects.equals(documentData, taskDocumentData)) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+ "For Intent " + intent + " bringing to top: " + r.intent);
+ mRecord = r;
+ mIdealMatch = true;
+ return true;
+ } else if (!isDocument && !taskIsDocument
+ && mRecord == null && task.rootAffinity != null) {
+ if (task.rootAffinity.equals(mTarget.taskAffinity)) {
+ if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
+ // It is possible for multiple tasks to have the same root affinity especially
+ // if they are in separate stacks. We save off this candidate, but keep looking
+ // to see if there is a better candidate.
+ mRecord = r;
+ mIdealMatch = false;
+ }
+ } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
+
+ return false;
+ }
}
RootActivityContainer(ActivityTaskManagerService service) {
@@ -772,27 +890,21 @@
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final ActivityStack stack = display.getFocusedStack();
- if (stack != null) {
- stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
- final ActivityRecord top = stack.topRunningActivityLocked();
- final int size = mTmpActivityList.size();
- for (int i = 0; i < size; i++) {
- final ActivityRecord activity = mTmpActivityList.get(i);
- if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
- && processName.equals(activity.processName)) {
- try {
- if (mStackSupervisor.realStartActivityLocked(activity, app,
- top == activity /* andResume */, true /* checkConfig */)) {
- didSomething = true;
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception in new application when starting activity "
- + top.intent.getComponent().flattenToShortString(), e);
- throw e;
- }
- }
- }
+ if (stack == null) {
+ continue;
}
+
+ mTmpRemoteException = null;
+ mTmpBoolean = false; // Set to true if an activity was started.
+ final PooledFunction c = PooledLambda.obtainFunction(
+ RootActivityContainer::startActivityForAttachedApplicationIfNeeded, this,
+ PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivityLocked());
+ stack.forAllActivities(c);
+ c.recycle();
+ if (mTmpRemoteException != null) {
+ throw mTmpRemoteException;
+ }
+ didSomething |= mTmpBoolean;
}
if (!didSomething) {
ensureActivitiesVisible(null, 0, false /* preserve_windows */);
@@ -800,6 +912,27 @@
return didSomething;
}
+ private boolean startActivityForAttachedApplicationIfNeeded(ActivityRecord r,
+ WindowProcessController app, ActivityRecord top) {
+ if (r.finishing || !r.okToShowLocked() || !r.visibleIgnoringKeyguard || r.app != null
+ || app.mUid != r.info.applicationInfo.uid || !app.mName.equals(r.processName)) {
+ return false;
+ }
+
+ try {
+ if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/,
+ true /*checkConfig*/)) {
+ mTmpBoolean = true;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception in new application when starting activity "
+ + top.intent.getComponent().flattenToShortString(), e);
+ mTmpRemoteException = e;
+ return true;
+ }
+ return false;
+ }
+
/**
* Make sure that all activities that need to be visible in the system actually are and update
* their configuration.
@@ -1526,14 +1659,12 @@
}
}
- void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.addStartingWindowsForVisibleActivities(taskSwitch);
+ void addStartingWindowsForVisibleActivities() {
+ mRootWindowContainer.forAllActivities((r) -> {
+ if (r.mVisibleRequested) {
+ r.showStartingWindow(null /* prev */, false /* newTask */, true /*taskSwitch*/);
}
- }
+ });
}
void invalidateTaskLayers() {
@@ -1541,27 +1672,37 @@
}
void rankTaskLayersIfNeeded() {
- if (!mTaskLayersChanged) {
+ if (!mTaskLayersChanged || mRootWindowContainer == null) {
return;
}
mTaskLayersChanged = false;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- int baseLayer = 0;
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- baseLayer += stack.rankTaskLayers(baseLayer);
- }
+ mTmpTaskLayerRank = 0;
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RootActivityContainer::rankTaskLayerForActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ mRootWindowContainer.forAllActivities(c);
+ c.recycle();
+ }
+
+ private void rankTaskLayerForActivity(ActivityRecord r) {
+ if (r.canBeTopRunning() && r.mVisibleRequested) {
+ r.getTask().mLayerRank = ++mTmpTaskLayerRank;
+ } else {
+ r.getTask().mLayerRank = -1;
}
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.clearOtherAppTimeTrackers(except);
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RootActivityContainer::clearOtherAppTimeTrackers,
+ PooledLambda.__(ActivityRecord.class), except);
+ mRootWindowContainer.forAllActivities(c);
+ c.recycle();
+ }
+
+ private static void clearOtherAppTimeTrackers(ActivityRecord r, AppTimeTracker except) {
+ if ( r.appTimeTracker != except) {
+ r.appTimeTracker = null;
}
}
@@ -1575,30 +1716,6 @@
}
}
- void releaseSomeActivitiesLocked(WindowProcessController app, String reason) {
- // Tasks is non-null only if two or more tasks are found.
- ArraySet<Task> tasks = app.getReleaseSomeActivitiesTasks();
- if (tasks == null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Didn't find two or more tasks to release");
- return;
- }
- // If we have activities in multiple tasks that are in a position to be destroyed,
- // let's iterate through the tasks and release the oldest one.
- final int numDisplays = mActivityDisplays.size();
- for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int stackCount = display.getChildCount();
- // Step through all stacks starting from behind, to hit the oldest things first.
- for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- // Try to release activities in this stack; if we manage to, we are done.
- if (stack.releaseSomeActivitiesLocked(app, tasks, reason) > 0) {
- return;
- }
- }
- }
- }
-
// Tries to put all activity stacks to sleep. Returns true if all stacks were
// successfully put to sleep.
boolean putStacksToSleep(boolean allowDelay, boolean shuttingDown) {
@@ -1625,28 +1742,52 @@
}
void handleAppCrash(WindowProcessController app) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.handleAppCrash(app);
- }
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RootActivityContainer::handleAppCrash, PooledLambda.__(ActivityRecord.class), app);
+ mRootWindowContainer.forAllActivities(c);
+ c.recycle();
+ }
+
+ private static void handleAppCrash(ActivityRecord r, WindowProcessController app) {
+ if (r.app != app) return;
+ Slog.w(TAG, " Force finishing activity "
+ + r.intent.getComponent().flattenToShortString());
+ // Force the destroy to skip right to removal.
+ r.app = null;
+ r.getDisplay().mDisplayContent.prepareAppTransition(
+ TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
+ r.destroyIfPossible("handleAppCrashed");
}
ActivityRecord findActivity(Intent intent, ActivityInfo info, boolean compareIntentFilters) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- final ActivityRecord ar = stack.findActivityLocked(
- intent, info, compareIntentFilters);
- if (ar != null) {
- return ar;
- }
+ ComponentName cls = intent.getComponent();
+ if (info.targetActivity != null) {
+ cls = new ComponentName(info.packageName, info.targetActivity);
+ }
+ final int userId = UserHandle.getUserId(info.applicationInfo.uid);
+
+ final PooledPredicate p = PooledLambda.obtainPredicate(
+ RootActivityContainer::matchesActivity, PooledLambda.__(ActivityRecord.class),
+ userId, compareIntentFilters, intent, cls);
+ final ActivityRecord r = mRootWindowContainer.getActivity(p);
+ p.recycle();
+ return r;
+ }
+
+ private static boolean matchesActivity(ActivityRecord r, int userId,
+ boolean compareIntentFilters, Intent intent, ComponentName cls) {
+ if (!r.canBeTopRunning() || r.mUserId != userId) return false;
+
+ if (compareIntentFilters) {
+ if (r.intent.filterEquals(intent)) {
+ return true;
+ }
+ } else {
+ if (r.intent.getComponent().equals(cls)) {
+ return true;
}
}
- return null;
+ return false;
}
boolean hasAwakeDisplay() {
@@ -1978,39 +2119,104 @@
}
void closeSystemDialogs() {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.closeSystemDialogsLocked();
+ mRootWindowContainer.forAllActivities((r) -> {
+ if ((r.info.flags & ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
+ r.finishIfPossible("close-sys", true /* oomAdj */);
}
+ });
+ }
+
+ FinishDisabledPackageActivitiesHelper mFinishDisabledPackageActivitiesHelper =
+ new FinishDisabledPackageActivitiesHelper();
+ class FinishDisabledPackageActivitiesHelper {
+ private boolean mDidSomething;
+ private String mPackageName;
+ private Set<String> mFilterByClasses;
+ private boolean mDoit;
+ private boolean mEvenPersistent;
+ private int mUserId;
+ private Task mLastTask;
+ private ComponentName mHomeActivity;
+
+ private void reset(String packageName, Set<String> filterByClasses,
+ boolean doit, boolean evenPersistent, int userId) {
+ mDidSomething = false;
+ mPackageName = packageName;
+ mFilterByClasses = filterByClasses;
+ mDoit = doit;
+ mEvenPersistent = evenPersistent;
+ mUserId = userId;
+ mLastTask = null;
+ mHomeActivity = null;
+ }
+
+ boolean process(String packageName, Set<String> filterByClasses,
+ boolean doit, boolean evenPersistent, int userId) {
+ reset(packageName, filterByClasses, doit, evenPersistent, userId);
+
+ final PooledFunction f = PooledLambda.obtainFunction(
+ FinishDisabledPackageActivitiesHelper::processActivity, this,
+ PooledLambda.__(ActivityRecord.class));
+ mRootWindowContainer.forAllActivities(f);
+ f.recycle();
+ return mDidSomething;
+ }
+
+ private boolean processActivity(ActivityRecord r) {
+ final boolean sameComponent =
+ (r.packageName.equals(mPackageName) && (mFilterByClasses == null
+ || mFilterByClasses.contains(r.mActivityComponent.getClassName())))
+ || (mPackageName == null && r.mUserId == mUserId);
+ if ((mUserId == UserHandle.USER_ALL || r.mUserId == mUserId)
+ && (sameComponent || r.getTask() == mLastTask)
+ && (r.app == null || mEvenPersistent || !r.app.isPersistent())) {
+ if (!mDoit) {
+ if (r.finishing) {
+ // If this activity is just finishing, then it is not
+ // interesting as far as something to stop.
+ return false;
+ }
+ return true;
+ }
+ if (r.isActivityTypeHome()) {
+ if (mHomeActivity != null && mHomeActivity.equals(r.mActivityComponent)) {
+ Slog.i(TAG, "Skip force-stop again " + r);
+ return false;
+ } else {
+ mHomeActivity = r.mActivityComponent;
+ }
+ }
+ mDidSomething = true;
+ Slog.i(TAG, " Force finishing activity " + r);
+ mLastTask = r.getTask();
+ r.finishIfPossible("force-stop", true);
+ }
+
+ return false;
}
}
/** @return true if some activity was finished (or would have finished if doit were true). */
boolean finishDisabledPackageActivities(String packageName, Set<String> filterByClasses,
boolean doit, boolean evenPersistent, int userId) {
- boolean didSomething = false;
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (stack.finishDisabledPackageActivitiesLocked(
- packageName, filterByClasses, doit, evenPersistent, userId)) {
- didSomething = true;
- }
- }
- }
- return didSomething;
+ return mFinishDisabledPackageActivitiesHelper.process(packageName, filterByClasses, doit,
+ evenPersistent, userId);
}
void updateActivityApplicationInfo(ApplicationInfo aInfo) {
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- stack.updateActivityApplicationInfoLocked(aInfo);
- }
+ final String packageName = aInfo.packageName;
+ final int userId = UserHandle.getUserId(aInfo.uid);
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ RootActivityContainer::updateActivityApplicationInfo,
+ PooledLambda.__(ActivityRecord.class), aInfo, userId, packageName);
+ mRootWindowContainer.forAllActivities(c);
+ c.recycle();
+ }
+
+ private static void updateActivityApplicationInfo(
+ ActivityRecord r, ApplicationInfo aInfo, int userId, String packageName) {
+ if (r.mUserId == userId && packageName.equals(r.packageName)) {
+ r.updateApplicationInfo(aInfo);
}
}
@@ -2063,7 +2269,7 @@
// If the focused stack is not null or not empty, there should have some activities
// resuming or resumed. Make sure these activities are idle.
final ActivityStack stack = display.getFocusedStack();
- if (stack == null || stack.numActivities() == 0) {
+ if (stack == null || !stack.hasActivity()) {
continue;
}
final ActivityRecord resumedActivity = stack.getResumedActivity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 619b71c..88a38e0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -67,7 +67,6 @@
import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
import static com.android.server.am.TaskRecordProto.STACK_ID;
import static com.android.server.am.TaskRecordProto.TASK;
-import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -138,6 +137,10 @@
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.XmlUtils;
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.protolog.common.ProtoLog;
import com.android.server.wm.ActivityStack.ActivityState;
@@ -152,8 +155,9 @@
import java.util.ArrayList;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.function.Predicate;
-class Task extends WindowContainer<ActivityRecord> implements ConfigurationContainerListener {
+class Task extends WindowContainer<WindowContainer> {
private static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_ATM;
private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
@@ -382,6 +386,44 @@
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
+ private static Exception sTmpException;
+
+ private final FindRootHelper mFindRootHelper = new FindRootHelper();
+ private class FindRootHelper {
+ private ActivityRecord mRoot;
+
+ private void clear() {
+ mRoot = null;
+ }
+
+ ActivityRecord findRoot(boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
+ final PooledFunction f = PooledLambda.obtainFunction(FindRootHelper::processActivity,
+ this, PooledLambda.__(ActivityRecord.class), ignoreRelinquishIdentity,
+ setToBottomIfNone);
+ clear();
+ forAllActivities(f, false /*traverseTopToBottom*/);
+ f.recycle();
+ return mRoot;
+ }
+
+ private boolean processActivity(ActivityRecord r,
+ boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
+ if (mRoot == null && setToBottomIfNone) {
+ // This is the first activity we are process. Set it as the candidate root in case
+ // we don't find a better one.
+ mRoot = r;
+ }
+
+ if (r.finishing) return false;
+
+ // Set this as the candidate root since it isn't finishing.
+ mRoot = r;
+
+ // Only end search if we are ignore relinquishing identity or we are not relinquishing.
+ return ignoreRelinquishIdentity || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+ }
+ }
+
/**
* Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
* ActivityInfo, Intent, TaskDescription)} instead.
@@ -464,7 +506,7 @@
mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
}
- void cleanUpResourcesForDestroy() {
+ private void cleanUpResourcesForDestroy() {
if (hasChild()) {
return;
}
@@ -931,10 +973,11 @@
super.onParentChanged(newParent, oldParent);
if (oldStack != null) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord activity = getChildAt(i);
- oldStack.onActivityRemovedFromStack(activity);
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityStack::onActivityRemovedFromStack, oldStack,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(c);
+ c.recycle();
if (oldStack.inPinnedWindowingMode()
&& (newStack == null || !newStack.inPinnedWindowingMode())) {
@@ -945,10 +988,11 @@
}
if (newStack != null) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord activity = getChildAt(i);
- newStack.onActivityAddedToStack(activity);
- }
+ final PooledConsumer c = PooledLambda.obtainConsumer(
+ ActivityStack::onActivityAddedToStack, newStack,
+ PooledLambda.__(ActivityRecord.class));
+ forAllActivities(c);
+ c.recycle();
// TODO: Ensure that this is actually necessary here
// Notify the voice session if required
@@ -1068,25 +1112,11 @@
}
ActivityRecord getRootActivity(boolean setToBottomIfNone) {
- return getRootActivity(false /*ignoreRelinquishIdentity*/, false /*setToBottomIfNone*/);
+ return getRootActivity(false /*ignoreRelinquishIdentity*/, setToBottomIfNone);
}
ActivityRecord getRootActivity(boolean ignoreRelinquishIdentity, boolean setToBottomIfNone) {
- ActivityRecord root;
- if (ignoreRelinquishIdentity) {
- root = getActivity((r) -> !r.finishing, false /*traverseTopToBottom*/);
- } else {
- root = getActivity((r) ->
- !r.finishing && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0,
- false /*traverseTopToBottom*/);
- }
-
- if (root == null && setToBottomIfNone) {
- // All activities in the task are either finishing or relinquish task identity.
- // But we still want to update the intent, so let's use the bottom activity.
- root = getActivity((r) -> true, false /*traverseTopToBottom*/);
- }
- return root;
+ return mFindRootHelper.findRoot(ignoreRelinquishIdentity, setToBottomIfNone);
}
ActivityRecord getTopNonFinishingActivity() {
@@ -1094,101 +1124,42 @@
}
ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord r = getChildAt(i);
- if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
- continue;
- }
- return r;
- }
- return null;
+ return getTopActivity(false /*includeFinishing*/, includeOverlays);
}
ActivityRecord topRunningActivityLocked() {
- if (mStack != null) {
- for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = getChildAt(activityNdx);
- if (!r.finishing && r.okToShowLocked()) {
- return r;
- }
- }
+ if (getParent() == null) {
+ return null;
}
- return null;
+ return getActivity(ActivityRecord::canBeTopRunning);
}
/**
* Return true if any activities in this task belongs to input uid.
*/
- boolean containsAppUid(int uid) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord r = getChildAt(i);
- if (r.getUid() == uid) {
- return true;
- }
- }
- return false;
- }
-
- void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
- if (mStack != null) {
- for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = getChildAt(activityNdx);
- if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
- outActivities.add(r);
- }
- }
- }
+ boolean isUidPresent(int uid) {
+ final PooledPredicate p = PooledLambda.obtainPredicate(
+ ActivityRecord::isUid, PooledLambda.__(ActivityRecord.class), uid);
+ final boolean isUidPresent = getActivity(p) != null;
+ p.recycle();
+ return isUidPresent;
}
ActivityRecord topRunningActivityWithStartingWindowLocked() {
- if (mStack != null) {
- for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = getChildAt(activityNdx);
- if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
- || r.finishing || !r.okToShowLocked()) {
- continue;
- }
- return r;
- }
+ if (getParent() == null) {
+ return null;
}
- return null;
+ return getActivity((r) -> r.mStartingWindowState == STARTING_WINDOW_SHOWN
+ && r.canBeTopRunning());
}
/**
* Return the number of running activities, and the number of non-finishing/initializing
* activities in the provided {@param reportOut} respectively.
*/
- void getNumRunningActivities(TaskActivitiesReport reportOut) {
+ private void getNumRunningActivities(TaskActivitiesReport reportOut) {
reportOut.reset();
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord r = getChildAt(i);
- if (r.finishing) {
- continue;
- }
-
- reportOut.base = r;
-
- // Increment the total number of non-finishing activities
- reportOut.numActivities++;
-
- if (reportOut.top == null || (reportOut.top.isState(ActivityState.INITIALIZING))) {
- reportOut.top = r;
- // Reset the number of running activities until we hit the first non-initializing
- // activity
- reportOut.numRunning = 0;
- }
- if (r.attachedToProcess()) {
- // Increment the number of actually running activities
- reportOut.numRunning++;
- }
- }
- }
-
- boolean okToShowLocked() {
- // NOTE: If {@link Task#topRunningActivity} return is not null then it is
- // okay to show the activity when locked.
- return mAtmService.mStackSupervisor.isCurrentProfileLocked(mUserId)
- || topRunningActivityLocked() != null;
+ forAllActivities(reportOut);
}
/**
@@ -1212,10 +1183,11 @@
}
@Override
- void addChild(ActivityRecord r, int index) {
+ void addChild(WindowContainer child, int index) {
// If this task had any child before we added this one.
boolean hadChild = hasChild();
+ final ActivityRecord r = (ActivityRecord) child;
index = getAdjustedAddPosition(r, index);
super.addChild(r, index);
@@ -1244,9 +1216,6 @@
}
updateEffectiveIntent();
- if (r.isPersistable()) {
- mAtmService.notifyTaskPersisterLocked(this, false);
- }
// Make sure the list of display UID whitelists is updated
// now that this record is in a new task.
@@ -1258,16 +1227,13 @@
}
@Override
- void removeChild(ActivityRecord r) {
+ void removeChild(WindowContainer r) {
if (!mChildren.contains(r)) {
Slog.e(TAG, "removeChild: r=" + r + " not found in t=" + this);
return;
}
super.removeChild(r);
- if (r.isPersistable()) {
- mAtmService.notifyTaskPersisterLocked(this, false);
- }
if (inPinnedWindowingMode()) {
// We normally notify listeners of task stack changes on pause, however pinned stack
@@ -1283,7 +1249,7 @@
// The following block can be executed multiple times if there is more than one overlay.
// {@link ActivityStackSupervisor#removeTaskByIdLocked} handles this by reverse lookup
// of the task by id and exiting early if not found.
- if (onlyHasTaskOverlayActivities(false /* excludingFinishing */)) {
+ if (onlyHasTaskOverlayActivities(true /*includeFinishing*/)) {
// When destroying a task, tell the supervisor to remove it so that any activity it
// has can be cleaned up correctly. This is currently the only place where we remove
// a task with the DESTROYING mode, so instead of passing the onlyHasTaskOverlays
@@ -1305,25 +1271,20 @@
/**
* @return whether or not there are ONLY task overlay activities in the stack.
- * If {@param excludeFinishing} is set, then ignore finishing activities in the check.
- * If there are no task overlay activities, this call returns false.
+ * If {@param includeFinishing} is set, then don't ignore finishing activities in the
+ * check. If there are no task overlay activities, this call returns false.
*/
- boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
- int count = 0;
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final ActivityRecord r = getChildAt(i);
- if (excludeFinishing && r.finishing) {
- continue;
- }
- if (!r.mTaskOverlay) {
- return false;
- }
- count++;
+ boolean onlyHasTaskOverlayActivities(boolean includeFinishing) {
+ if (getChildCount() == 0) {
+ return false;
}
- return count > 0;
+ if (includeFinishing) {
+ return getActivity((r) -> r.mTaskOverlay) != null;
+ }
+ return getActivity((r) -> !r.finishing && r.mTaskOverlay) != null;
}
- boolean autoRemoveFromRecents() {
+ private boolean autoRemoveFromRecents() {
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
// the user.
@@ -1335,24 +1296,21 @@
* task starting at a specified index.
*/
private void performClearTaskAtIndexLocked(String reason) {
- int numActivities = getChildCount();
- for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (r.finishing) {
- continue;
- }
- if (mStack == null) {
+ // Broken down into to cases to avoid object create due to capturing mStack.
+ if (mStack == null) {
+ forAllActivities((r) -> {
+ if (r.finishing) return;
// Task was restored from persistent storage.
r.takeFromHistory();
removeChild(r);
- --activityNdx;
- --numActivities;
- } else if (r.finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
- false /* oomAdj */)
- == FINISH_RESULT_REMOVED) {
- --activityNdx;
- --numActivities;
- }
+ });
+ } else {
+ forAllActivities((r) -> {
+ if (r.finishing) return;
+ // TODO: figure-out how to avoid object creation due to capture of reason variable.
+ r.finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
+ false /* oomAdj */);
+ });
}
}
@@ -1383,50 +1341,43 @@
* @return Returns the old activity that should be continued to be used,
* or {@code null} if none was found.
*/
- final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
- int numActivities = getChildCount();
- for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = getChildAt(activityNdx);
- if (r.finishing) {
- continue;
- }
- if (r.mActivityComponent.equals(newR.mActivityComponent)) {
- // Here it is! Now finish everything in front...
- final ActivityRecord ret = r;
+ private ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
+ final ActivityRecord r = findActivityInHistory(newR.mActivityComponent);
+ if (r == null) return null;
- for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
- r = getChildAt(activityNdx);
- if (r.finishing) {
- continue;
- }
- ActivityOptions opts = r.takeOptionsLocked(false /* fromClient */);
- if (opts != null) {
- ret.updateOptionsLocked(opts);
- }
- if (r.finishIfPossible("clear-task-stack", false /* oomAdj */)
- == FINISH_RESULT_REMOVED) {
- --activityNdx;
- --numActivities;
- }
- }
+ final PooledFunction f = PooledLambda.obtainFunction(Task::finishActivityAbove,
+ PooledLambda.__(ActivityRecord.class), r);
+ forAllActivities(f);
+ f.recycle();
- // Finally, if this is a normal launch mode (that is, not
- // expecting onNewIntent()), then we will finish the current
- // instance of the activity so a new fresh one can be started.
- if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
- && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
- && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
- if (!ret.finishing) {
- ret.finishIfPossible("clear-task-top", false /* oomAdj */);
- return null;
- }
- }
-
- return ret;
+ // Finally, if this is a normal launch mode (that is, not expecting onNewIntent()), then we
+ // will finish the current instance of the activity so a new fresh one can be started.
+ if (r.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
+ && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
+ if (!r.finishing) {
+ r.finishIfPossible("clear-task-top", false /* oomAdj */);
+ return null;
}
}
- return null;
+ return r;
+ }
+
+ private static boolean finishActivityAbove(ActivityRecord r, ActivityRecord boundaryActivity) {
+ // Stop operation once we reach the boundary activity.
+ if (r == boundaryActivity) return true;
+
+ if (!r.finishing) {
+ final ActivityOptions opts = r.takeOptionsLocked(false /* fromClient */);
+ if (opts != null) {
+ // TODO: Why is this updating the boundary activity vs. the current activity???
+ boundaryActivity.updateOptionsLocked(opts);
+ }
+ r.finishIfPossible("clear-task-stack", false /* oomAdj */);
+ }
+
+ return false;
}
void removeTaskActivitiesLocked(String reason) {
@@ -1537,140 +1488,83 @@
* Find the activity in the history stack within the given task. Returns
* the index within the history at which it's found, or < 0 if not found.
*/
- final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
- final ComponentName realActivity = r.mActivityComponent;
- for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord candidate = getChildAt(activityNdx);
- if (candidate.finishing) {
- continue;
- }
- if (candidate.mActivityComponent.equals(realActivity)) {
- return candidate;
- }
- }
- return null;
+ ActivityRecord findActivityInHistory(ComponentName component) {
+ final PooledPredicate p = PooledLambda.obtainPredicate(Task::matchesActivityInHistory,
+ PooledLambda.__(ActivityRecord.class), component);
+ final ActivityRecord r = getActivity(p);
+ p.recycle();
+ return r;
+ }
+
+ private static boolean matchesActivityInHistory(
+ ActivityRecord r, ComponentName activityComponent) {
+ return !r.finishing && r.mActivityComponent.equals(activityComponent);
}
/** Updates the last task description values. */
void updateTaskDescription() {
- // TODO(AM refactor): Cleanup to use findRootIndex()
- // Traverse upwards looking for any break between main task activities and
- // utility activities.
- int activityNdx;
- final int numActivities = getChildCount();
- final boolean relinquish = numActivities != 0
- && (getChildAt(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
- for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
- // This will be the top activity for determining taskDescription. Pre-inc to
- // overcome initial decrement below.
- ++activityNdx;
- break;
- }
- if (r.intent != null
- && (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
- break;
- }
+ final ActivityRecord root = getRootActivity(true);
+ if (root == null) return;
+
+ final TaskDescription taskDescription = new TaskDescription();
+ final PooledFunction f = PooledLambda.obtainFunction(
+ Task::setTaskDescriptionFromActivityAboveRoot,
+ PooledLambda.__(ActivityRecord.class), root, taskDescription);
+ forAllActivities(f);
+ f.recycle();
+ taskDescription.setResizeMode(mResizeMode);
+ taskDescription.setMinWidth(mMinWidth);
+ taskDescription.setMinHeight(mMinHeight);
+ setTaskDescription(taskDescription);
+ // Update the task affiliation color if we are the parent of the group
+ if (mTaskId == mAffiliatedTaskId) {
+ mAffiliatedTaskColor = taskDescription.getPrimaryColor();
}
- if (activityNdx > 0) {
- // Traverse downwards starting below break looking for set label, icon.
- // Note that if there are activities in the task but none of them set the
- // recent activity values, then we do not fall back to the last set
- // values in the Task.
- String label = null;
- String iconFilename = null;
- int iconResource = -1;
- int colorPrimary = 0;
- int colorBackground = 0;
- int statusBarColor = 0;
- int navigationBarColor = 0;
- boolean statusBarContrastWhenTransparent = false;
- boolean navigationBarContrastWhenTransparent = false;
- boolean topActivity = true;
- for (--activityNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (r.mTaskOverlay) {
- continue;
- }
- if (r.taskDescription != null) {
- if (label == null) {
- label = r.taskDescription.getLabel();
- }
- if (iconResource == -1) {
- iconResource = r.taskDescription.getIconResource();
- }
- if (iconFilename == null) {
- iconFilename = r.taskDescription.getIconFilename();
- }
- if (colorPrimary == 0) {
- colorPrimary = r.taskDescription.getPrimaryColor();
- }
- if (topActivity) {
- colorBackground = r.taskDescription.getBackgroundColor();
- statusBarColor = r.taskDescription.getStatusBarColor();
- navigationBarColor = r.taskDescription.getNavigationBarColor();
- statusBarContrastWhenTransparent =
- r.taskDescription.getEnsureStatusBarContrastWhenTransparent();
- navigationBarContrastWhenTransparent =
- r.taskDescription.getEnsureNavigationBarContrastWhenTransparent();
- }
- }
- topActivity = false;
- }
- final TaskDescription taskDescription = new TaskDescription(label, null, iconResource,
- iconFilename, colorPrimary, colorBackground, statusBarColor, navigationBarColor,
- statusBarContrastWhenTransparent, navigationBarContrastWhenTransparent,
- mResizeMode, mMinWidth, mMinHeight);
- setTaskDescription(taskDescription);
- // Update the task affiliation color if we are the parent of the group
- if (mTaskId == mAffiliatedTaskId) {
- mAffiliatedTaskColor = taskDescription.getPrimaryColor();
- }
- mAtmService.getTaskChangeNotificationController().notifyTaskDescriptionChanged(
- getTaskInfo());
- }
+ mAtmService.getTaskChangeNotificationController().notifyTaskDescriptionChanged(
+ getTaskInfo());
}
- /**
- * Find the index of the root activity in the task. It will be the first activity from the
- * bottom that is not finishing.
- *
- * @param effectiveRoot Flag indicating whether 'effective root' should be returned - an
- * activity that defines the task identity (its base intent). It's the
- * first one that does not have
- * {@link ActivityInfo#FLAG_RELINQUISH_TASK_IDENTITY}.
- * @return index of the 'root' or 'effective' root in the list of activities, -1 if no eligible
- * activity was found.
- */
- int findRootIndex(boolean effectiveRoot) {
- int effectiveNdx = -1;
- final int topActivityNdx = getChildCount() - 1;
- for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (r.finishing) {
- continue;
+ private static boolean setTaskDescriptionFromActivityAboveRoot(
+ ActivityRecord r, ActivityRecord root, TaskDescription td) {
+ if (!r.mTaskOverlay && r.taskDescription != null) {
+ final TaskDescription atd = r.taskDescription;
+ if (td.getLabel() == null) {
+ td.setLabel(atd.getLabel());
}
- effectiveNdx = activityNdx;
- if (!effectiveRoot || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
- break;
+ if (td.getIconResource() == 0) {
+ td.setIcon(atd.getIconResource());
}
+ if (td.getIconFilename() == null) {
+ td.setIconFilename(atd.getIconFilename());
+ }
+ if (td.getPrimaryColor() == 0) {
+ td.setPrimaryColor(atd.getPrimaryColor());
+ }
+ if (td.getBackgroundColor() == 0) {
+ td.setBackgroundColor(atd.getBackgroundColor());
+ }
+ if (td.getStatusBarColor() == 0) {
+ td.setStatusBarColor(atd.getStatusBarColor());
+ td.setEnsureStatusBarContrastWhenTransparent(
+ atd.getEnsureStatusBarContrastWhenTransparent());
+ }
+ if (td.getNavigationBarColor() == 0) {
+ td.setNavigationBarColor(atd.getNavigationBarColor());
+ td.setEnsureNavigationBarContrastWhenTransparent(
+ atd.getEnsureNavigationBarContrastWhenTransparent());
+ }
+
}
- return effectiveNdx;
+
+ // End search once we get to root.
+ return r == root;
}
// TODO (AM refactor): Invoke automatically when there is a change in children
@VisibleForTesting
void updateEffectiveIntent() {
- int effectiveRootIndex = findRootIndex(true /* effectiveRoot */);
- if (effectiveRootIndex == -1) {
- // All activities in the task are either finishing or relinquish task identity.
- // But we still want to update the intent, so let's use the bottom activity.
- effectiveRootIndex = 0;
- }
- final ActivityRecord r = getChildAt(effectiveRootIndex);
- setIntent(r);
-
+ final ActivityRecord root = getRootActivity(true /*setToBottomIfNone*/);
+ setIntent(root);
// Update the task description when the activities change
updateTaskDescription();
}
@@ -2220,15 +2114,6 @@
return mLastNonFullscreenBounds;
}
- void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (r.mVisibleRequested) {
- r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
- }
- }
- }
-
void setRootProcess(WindowProcessController proc) {
clearRootProcess();
if (intent != null
@@ -2245,12 +2130,6 @@
}
}
- void clearAllPendingOptions() {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- getChildAt(i).clearOptionsLocked(false /* withAbort */);
- }
- }
-
@Override
DisplayContent getDisplayContent() {
return getTaskStack() != null ? getTaskStack().getDisplayContent() : null;
@@ -2260,20 +2139,14 @@
return (ActivityStack) getParent();
}
+ // TODO(task-hierarchy): Needs to take a generic WindowManager when task contains other tasks.
int getAdjustedAddPosition(ActivityRecord r, int suggestedPosition) {
int maxPosition = mChildren.size();
if (!r.mTaskOverlay) {
// We want to place all non-overlay activities below overlays.
- while (maxPosition > 0) {
- final ActivityRecord current = mChildren.get(maxPosition - 1);
- if (current.mTaskOverlay) {
- --maxPosition;
- continue;
- }
- break;
- }
- if (maxPosition < 0) {
- maxPosition = 0;
+ final ActivityRecord bottomMostOverlay = getActivity((ar) -> ar.mTaskOverlay, false);
+ if (bottomMostOverlay != null) {
+ maxPosition = Math.max(mChildren.indexOf(bottomMostOverlay) - 1, 0);
}
}
@@ -2281,18 +2154,13 @@
}
@Override
- void positionChildAt(int position, ActivityRecord child, boolean includingParents) {
- position = getAdjustedAddPosition(child, position);
+ void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+ position = getAdjustedAddPosition((ActivityRecord) child, position);
super.positionChildAt(position, child, includingParents);
}
private boolean hasWindowsAlive() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- if (mChildren.get(i).hasWindowsAlive()) {
- return true;
- }
- }
- return false;
+ return getActivity(ActivityRecord::hasWindowsAlive) != null;
}
@VisibleForTesting
@@ -2522,26 +2390,21 @@
* @param out Rect containing the max visible bounds.
* @return true if the task has some visible app windows; false otherwise.
*/
- private boolean getMaxVisibleBounds(Rect out) {
- boolean foundTop = false;
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord token = mChildren.get(i);
- // skip hidden (or about to hide) apps
- if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
- continue;
- }
- final WindowState win = token.findMainWindow();
- if (win == null) {
- continue;
- }
- if (!foundTop) {
- foundTop = true;
- out.setEmpty();
- }
-
- win.getMaxVisibleBounds(out);
+ private static void getMaxVisibleBounds(ActivityRecord token, Rect out, boolean[] foundTop) {
+ // skip hidden (or about to hide) apps
+ if (token.mIsExiting || !token.isClientVisible() || !token.mVisibleRequested) {
+ return;
}
- return foundTop;
+ final WindowState win = token.findMainWindow();
+ if (win == null) {
+ return;
+ }
+ if (!foundTop[0]) {
+ foundTop[0] = true;
+ out.setEmpty();
+ }
+
+ win.getMaxVisibleBounds(out);
}
/** Bounds of the task to be used for dimming, as well as touch related tests. */
@@ -2551,8 +2414,14 @@
// a DimLayer anyway if we weren't visible.
final boolean dockedResizing = displayContent != null
&& displayContent.mDividerControllerLocked.isResizing();
- if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
- return;
+ if (inFreeformWindowingMode()) {
+ boolean[] foundTop = { false };
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
+ PooledLambda.__(ActivityRecord.class), out, foundTop);
+ c.recycle();
+ if (foundTop[0]) {
+ return;
+ }
}
if (!matchParentBounds()) {
@@ -2658,8 +2527,9 @@
}
boolean showForAllUsers() {
- final int tokensCount = mChildren.size();
- return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
+ if (mChildren.isEmpty()) return false;
+ final ActivityRecord r = getTopNonFinishingActivity();
+ return r != null && r.mShowForAllUsers;
}
/**
@@ -2733,24 +2603,17 @@
}
ActivityRecord getTopFullscreenActivity() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- final WindowState win = activity.findMainWindow();
- if (win != null && win.mAttrs.isFullscreen()) {
- return activity;
- }
- }
- return null;
+ return getActivity((r) -> {
+ final WindowState win = r.findMainWindow();
+ return (win != null && win.mAttrs.isFullscreen());
+ });
}
ActivityRecord getTopVisibleActivity() {
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- if (!activity.mIsExiting && activity.isClientVisible() && activity.mVisibleRequested) {
- return activity;
- }
- }
- return null;
+ return getActivity((r) -> {
+ // skip hidden (or about to hide) apps
+ return !r.mIsExiting && r.isClientVisible() && r.mVisibleRequested;
+ });
}
void positionChildAtTop(ActivityRecord child) {
@@ -2806,6 +2669,13 @@
return callback.apply(this);
}
+ @Override
+ Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) {
+ // I'm a task!
+ // TODO(task-hierarchy): Change to traverse children when tasks can contain other tasks.
+ return callback.test(this) ? this : null;
+ }
+
/**
* @param canAffectSystemUiFlags If false, all windows in this task can not affect SystemUI
* flags. See {@link WindowState#canAffectSystemUiFlags()}.
@@ -2861,10 +2731,9 @@
final long token = proto.start(fieldId);
super.writeToProto(proto, WINDOW_CONTAINER, logLevel);
proto.write(TaskProto.ID, mTaskId);
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- activity.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
- }
+ forAllActivities((r) -> {
+ r.writeToProto(proto, APP_WINDOW_TOKENS, logLevel);
+ });
proto.write(FILLS_PARENT, matchParentBounds());
getBounds().writeToProto(proto, TaskProto.BOUNDS);
mOverrideDisplayedBounds.writeToProto(proto, DISPLAYED_BOUNDS);
@@ -2888,11 +2757,11 @@
final String triplePrefix = doublePrefix + " ";
final String quadruplePrefix = triplePrefix + " ";
- for (int i = mChildren.size() - 1; i >= 0; i--) {
- final ActivityRecord activity = mChildren.get(i);
- pw.println(triplePrefix + "Activity #" + i + " " + activity);
- activity.dump(pw, quadruplePrefix, dumpAll);
- }
+ int[] index = { 0 };
+ forAllActivities((r) -> {
+ pw.println(triplePrefix + "Activity #" + index[0]++ + " " + r);
+ r.dump(pw, quadruplePrefix, dumpAll);
+ });
}
/**
@@ -3071,10 +2940,10 @@
final long token = proto.start(fieldId);
writeToProtoInnerTaskOnly(proto, TASK, logLevel);
proto.write(com.android.server.am.TaskRecordProto.ID, mTaskId);
- for (int i = getChildCount() - 1; i >= 0; i--) {
- final ActivityRecord activity = getChildAt(i);
- activity.writeToProto(proto, ACTIVITIES);
- }
+
+ forAllActivities((r) -> {
+ r.writeToProto(proto, ACTIVITIES);
+ });
proto.write(STACK_ID, getStackId());
if (mLastNonFullscreenBounds != null) {
mLastNonFullscreenBounds.writeToProto(proto, LAST_NON_FULLSCREEN_BOUNDS);
@@ -3100,7 +2969,7 @@
}
/** @see #getNumRunningActivities(TaskActivitiesReport) */
- static class TaskActivitiesReport {
+ static class TaskActivitiesReport implements Consumer<ActivityRecord> {
int numRunning;
int numActivities;
ActivityRecord top;
@@ -3110,12 +2979,35 @@
numRunning = numActivities = 0;
top = base = null;
}
+
+ @Override
+ public void accept(ActivityRecord r) {
+ if (r.finishing) {
+ return;
+ }
+
+ base = r;
+
+ // Increment the total number of non-finishing activities
+ numActivities++;
+
+ if (top == null || (top.isState(ActivityState.INITIALIZING))) {
+ top = r;
+ // Reset the number of running activities until we hit the first non-initializing
+ // activity
+ numRunning = 0;
+ }
+ if (r.attachedToProcess()) {
+ // Increment the number of actually running activities
+ numRunning++;
+ }
+ }
}
/**
* Saves this {@link Task} to XML using given serializer.
*/
- void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+ void saveToXml(XmlSerializer out) throws Exception {
if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId));
@@ -3181,19 +3073,33 @@
out.endTag(null, TAG_INTENT);
}
- final int numActivities = getChildCount();
- for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = getChildAt(activityNdx);
- if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
- || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
- | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
- && activityNdx > 0) {
- // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
- break;
- }
+ sTmpException = null;
+ final PooledFunction f = PooledLambda.obtainFunction(Task::saveActivityToXml,
+ PooledLambda.__(ActivityRecord.class), getBottomMostActivity(), out);
+ forAllActivities(f);
+ f.recycle();
+ if (sTmpException != null) {
+ throw sTmpException;
+ }
+ }
+
+ private static boolean saveActivityToXml(
+ ActivityRecord r, ActivityRecord first, XmlSerializer out) {
+ if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
+ || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+ | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
+ && r != first) {
+ // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
+ return true;
+ }
+ try {
out.startTag(null, TAG_ACTIVITY);
r.saveToXml(out);
out.endTag(null, TAG_ACTIVITY);
+ return false;
+ } catch (Exception e) {
+ sTmpException = e;
+ return true;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 1e2f0d0..eb130a1 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -514,7 +514,7 @@
mService = service;
}
- private StringWriter saveToXml(Task task) throws IOException, XmlPullParserException {
+ private StringWriter saveToXml(Task task) throws Exception {
if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
final XmlSerializer xmlSerializer = new FastXmlSerializer();
StringWriter stringWriter = new StringWriter();
@@ -550,8 +550,7 @@
try {
if (DEBUG) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
- } catch (IOException e) {
- } catch (XmlPullParserException e) {
+ } catch (Exception e) {
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index d07516a..5cbab5d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -48,9 +48,9 @@
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
}
- final ActivityRecord top = task.getTopChild();
+ final ActivityRecord top = task.getTopMostActivity();
mAppTaskMap.put(top, task.mTaskId);
- mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
+ mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f83ceb0..c1a36c4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -235,23 +235,18 @@
* children, which should be ignored.
*/
@Nullable private ActivityRecord findAppTokenForSnapshot(Task task) {
- for (int i = task.getChildCount() - 1; i >= 0; --i) {
- final ActivityRecord activity = task.getChildAt(i);
- if (activity == null || !activity.isSurfaceShowing()
- || activity.findMainWindow() == null) {
- continue;
+ return task.getActivity((r) -> {
+ if (r == null || !r.isSurfaceShowing() || r.findMainWindow() == null) {
+ return false;
}
- final boolean hasVisibleChild = activity.forAllWindows(
+ return r.forAllWindows(
// Ensure at least one window for the top app is visible before attempting to
// take a screenshot. Visible here means that the WSA surface is shown and has
// an alpha greater than 0.
ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
&& ws.mWinAnimator.mLastAlpha > 0f, true /* traverseTopToBottom */);
- if (hasVisibleChild) {
- return activity;
- }
- }
- return null;
+
+ });
}
@Nullable
@@ -380,7 +375,7 @@
@VisibleForTesting
int getSnapshotMode(Task task) {
- final ActivityRecord topChild = task.getTopChild();
+ final ActivityRecord topChild = task.getTopMostActivity();
if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
@@ -395,7 +390,7 @@
* as possible by using the theme's window background.
*/
private TaskSnapshot drawAppThemeSnapshot(Task task) {
- final ActivityRecord topChild = task.getTopChild();
+ final ActivityRecord topChild = task.getTopMostActivity();
if (topChild == null) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 453514b2..5c1491e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -310,6 +310,10 @@
final protected void setParent(WindowContainer<WindowContainer> parent) {
final WindowContainer oldParent = mParent;
mParent = parent;
+
+ if (mParent != null) {
+ mParent.onChildAdded(this);
+ }
if (!mReparenting) {
onParentChanged(mParent, oldParent);
}
@@ -389,7 +393,6 @@
} else {
mChildren.add(positionToAdd, child);
}
- onChildAdded(child);
// Set the parent after we've actually added a child in case a subclass depends on this.
child.setParent(this);
@@ -418,7 +421,6 @@
}
mChildren.add(index, child);
- onChildAdded(child);
// Set the parent after we've actually added a child in case a subclass depends on this.
child.setParent(this);
@@ -1096,11 +1098,22 @@
}
boolean forAllActivities(Function<ActivityRecord, Boolean> callback) {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- if (mChildren.get(i).forAllActivities(callback)) {
- return true;
+ return forAllActivities(callback, true /*traverseTopToBottom*/);
+ }
+
+ boolean forAllActivities(
+ Function<ActivityRecord, Boolean> callback, boolean traverseTopToBottom) {
+ if (traverseTopToBottom) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (mChildren.get(i).forAllActivities(callback, traverseTopToBottom)) return true;
+ }
+ } else {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ if (mChildren.get(i).forAllActivities(callback, traverseTopToBottom)) return true;
}
}
+
return false;
}
@@ -1121,6 +1134,61 @@
}
}
+ /**
+ * Process all activities in this branch of the tree.
+ *
+ * @param callback Called for each activity found.
+ * @param boundary We don't return activities via {@param callback} until we get to this node in
+ * the tree.
+ * @param includeBoundary If the boundary from be processed to return activities.
+ * @param traverseTopToBottom direction to traverse the tree.
+ * @return {@code true} if we ended the search before reaching the end of the tree.
+ */
+ final boolean forAllActivities(Function<ActivityRecord, Boolean> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom) {
+ return forAllActivities(
+ callback, boundary, includeBoundary, traverseTopToBottom, new boolean[1]);
+ }
+
+ private boolean forAllActivities(Function<ActivityRecord, Boolean> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+ boolean[] boundaryFound) {
+ if (traverseTopToBottom) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ if (processForAllActivitiesWithBoundary(callback, boundary, includeBoundary,
+ traverseTopToBottom, boundaryFound, mChildren.get(i))) {
+ return true;
+ }
+ }
+ } else {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ if (processForAllActivitiesWithBoundary(callback, boundary, includeBoundary,
+ traverseTopToBottom, boundaryFound, mChildren.get(i))) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private boolean processForAllActivitiesWithBoundary(Function<ActivityRecord, Boolean> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+ boolean[] boundaryFound, WindowContainer wc) {
+ if (wc == boundary) {
+ boundaryFound[0] = true;
+ if (!includeBoundary) return false;
+ }
+
+ if (boundaryFound[0]) {
+ return wc.forAllActivities(callback, traverseTopToBottom);
+ }
+
+ return wc.forAllActivities(
+ callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
+ }
+
/** @return {@code true} if this node or any of its children contains an activity. */
boolean hasActivity() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
@@ -1156,6 +1224,81 @@
return null;
}
+ /**
+ * Gets an activity in a branch of the tree.
+ *
+ * @param callback called to test if this is the activity that should be returned.
+ * @param boundary We don't return activities via {@param callback} until we get to this node in
+ * the tree.
+ * @param includeBoundary If the boundary from be processed to return activities.
+ * @param traverseTopToBottom direction to traverse the tree.
+ * @return The activity if found or null.
+ */
+ final ActivityRecord getActivity(Predicate<ActivityRecord> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom) {
+ return getActivity(
+ callback, boundary, includeBoundary, traverseTopToBottom, new boolean[1]);
+ }
+
+ private ActivityRecord getActivity(Predicate<ActivityRecord> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+ boolean[] boundaryFound) {
+ if (traverseTopToBottom) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final ActivityRecord r = processGetActivityWithBoundary(callback, boundary,
+ includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+ if (r != null) {
+ return r;
+ }
+ }
+ } else {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ final ActivityRecord r = processGetActivityWithBoundary(callback, boundary,
+ includeBoundary, traverseTopToBottom, boundaryFound, mChildren.get(i));
+ if (r != null) {
+ return r;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private ActivityRecord processGetActivityWithBoundary(Predicate<ActivityRecord> callback,
+ WindowContainer boundary, boolean includeBoundary, boolean traverseTopToBottom,
+ boolean[] boundaryFound, WindowContainer wc) {
+ if (wc == boundary || boundary == null) {
+ boundaryFound[0] = true;
+ if (!includeBoundary) return null;
+ }
+
+ if (boundaryFound[0]) {
+ return wc.getActivity(callback, traverseTopToBottom);
+ }
+
+ return wc.getActivity(
+ callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
+ }
+
+ ActivityRecord getActivityAbove(ActivityRecord r) {
+ return getActivity((above) -> true, r,
+ false /*includeBoundary*/, false /*traverseTopToBottom*/);
+ }
+
+ ActivityRecord getActivityBelow(ActivityRecord r) {
+ return getActivity((below) -> true, r,
+ false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ }
+
+ ActivityRecord getBottomMostActivity() {
+ return getActivity((r) -> true, false /*traverseTopToBottom*/);
+ }
+
+ ActivityRecord getTopMostActivity() {
+ return getActivity((r) -> true, true /*traverseTopToBottom*/);
+ }
+
ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
// Break down into 4 calls to avoid object creation due to capturing input params.
if (includeFinishing) {
@@ -1187,6 +1330,31 @@
}
}
+ Task getTask(Predicate<Task> callback) {
+ return getTask(callback, true /*traverseTopToBottom*/);
+ }
+
+ Task getTask(Predicate<Task> callback, boolean traverseTopToBottom) {
+ if (traverseTopToBottom) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final Task t = mChildren.get(i).getTask(callback, traverseTopToBottom);
+ if (t != null) {
+ return t;
+ }
+ }
+ } else {
+ final int count = mChildren.size();
+ for (int i = 0; i < count; i++) {
+ final Task t = mChildren.get(i).getTask(callback, traverseTopToBottom);
+ if (t != null) {
+ return t;
+ }
+ }
+ }
+
+ return null;
+ }
+
/**
* For all tasks at or below this container call the callback.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index bbe9e4f..a6578f3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1104,6 +1104,7 @@
com.android.internal.R.bool.config_hasPermanentDpad);
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
+ inputManager.setInTouchMode(mInTouchMode);
mDrawLockTimeoutMillis = context.getResources().getInteger(
com.android.internal.R.integer.config_drawLockTimeoutMillis);
mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
@@ -3402,6 +3403,7 @@
synchronized (mGlobalLock) {
mInTouchMode = mode;
}
+ mInputManager.setInTouchMode(mode);
}
public void showEmulatorDisplayOverlayIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c1783ef..e169037 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -722,11 +722,10 @@
return true;
}
- ArraySet<Task> getReleaseSomeActivitiesTasks() {
+ void releaseSomeActivities(String reason) {
// Examine all activities currently running in the process.
- Task firstTask = null;
- // Tasks is non-null only if two or more tasks are found.
- ArraySet<Task> tasks = null;
+ // Candidate activities that can be destroyed.
+ ArrayList<ActivityRecord> candidates = null;
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Trying to release some activities in " + this);
for (int i = 0; i < mActivities.size(); i++) {
final ActivityRecord r = mActivities.get(i);
@@ -735,33 +734,37 @@
// down before we try to prune more activities.
if (r.finishing || r.isState(DESTROYING, DESTROYED)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Abort release; already destroying: " + r);
- return null;
+ return;
}
// Don't consider any activities that are currently not in a state where they
// can be destroyed.
- if (r.mVisibleRequested || !r.stopped || !r.hasSavedState()
+ if (r.mVisibleRequested || !r.stopped || !r.hasSavedState() || !r.isDestroyable()
|| r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) {
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r);
continue;
}
- final Task task = r.getTask();
- if (task != null) {
- if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Collecting release task " + task
- + " from " + r);
- if (firstTask == null) {
- firstTask = task;
- } else if (firstTask != task) {
- if (tasks == null) {
- tasks = new ArraySet<>();
- tasks.add(firstTask);
- }
- tasks.add(task);
+ if (r.getParent() != null) {
+ if (candidates == null) {
+ candidates = new ArrayList<>();
}
+ candidates.add(r);
}
}
- return tasks;
+ if (candidates != null) {
+ // Sort based on z-order in hierarchy.
+ candidates.sort(WindowContainer::compareTo);
+ // Release some older activities
+ int maxRelease = Math.max(candidates.size(), 1);
+ do {
+ final ActivityRecord r = candidates.remove(0);
+ if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + r
+ + " in state " + r.getState() + " for reason " + reason);
+ r.destroyImmediately(true /*removeFromApp*/, reason);
+ --maxRelease;
+ } while (maxRelease > 0);
+ }
}
public interface ComputeOomAdjCallback {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index dd2b27d..471164d 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -53,7 +53,6 @@
"com_android_server_am_LowMemDetector.cpp",
"onload.cpp",
":lib_networkStatsFactory_native",
- ":tethering-jni-srcs",
],
include_dirs: [
@@ -133,7 +132,6 @@
"android.hardware.power@1.0",
"android.hardware.power@1.1",
"android.hardware.power.stats@1.0",
- "android.hardware.tetheroffload.config@1.0",
"android.hardware.thermal@1.0",
"android.hardware.tv.cec@1.0",
"android.hardware.tv.input@1.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index b546a1d..9344a9b 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1502,6 +1502,13 @@
im->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled);
}
+static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */,
+ jlong ptr, jboolean inTouchMode) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->getInputManager()->getDispatcher()->setInTouchMode(inTouchMode);
+}
+
static jint nativeInjectInputEvent(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputEventObj, jint injectorPid, jint injectorUid,
jint syncMode, jint timeoutMillis, jint policyFlags) {
@@ -1781,6 +1788,8 @@
(void*) nativePilferPointers },
{ "nativeSetInputFilterEnabled", "(JZ)V",
(void*) nativeSetInputFilterEnabled },
+ { "nativeSetInTouchMode", "(JZ)V",
+ (void*) nativeSetInTouchMode },
{ "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIII)I",
(void*) nativeInjectInputEvent },
{ "nativeToggleCapsLock", "(JI)V",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 3bc2838..a0f5628 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -704,20 +704,22 @@
jfloatArray elevArray = env->NewFloatArray(listSize);
jfloatArray azimArray = env->NewFloatArray(listSize);
jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
+ jfloatArray basebandCn0Array = env->NewFloatArray(listSize);
jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
+ jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0);
/*
* Read GNSS SV info.
*/
for (size_t i = 0; i < listSize; ++i) {
enum ShiftWidth: uint8_t {
- SVID_SHIFT_WIDTH = 8,
- CONSTELLATION_TYPE_SHIFT_WIDTH = 4
+ SVID_SHIFT_WIDTH = 12,
+ CONSTELLATION_TYPE_SHIFT_WIDTH = 8
};
const IGnssCallback_V1_0::GnssSvInfo& info = getGnssSvInfoOfIndex(svStatus, i);
@@ -728,6 +730,8 @@
elev[i] = info.elevationDegrees;
azim[i] = info.azimuthDegrees;
carrierFreq[i] = info.carrierFrequencyHz;
+ // TODO(b/144850155): fill svidWithFlags with hasBasebandCn0DbHz based on HAL versions
+ basebandCn0s[i] = 0.0;
}
env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
@@ -735,10 +739,11 @@
env->ReleaseFloatArrayElements(elevArray, elev, 0);
env->ReleaseFloatArrayElements(azimArray, azim, 0);
env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
+ env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0);
env->CallVoidMethod(mCallbacksObj, method_reportSvStatus,
static_cast<jint>(listSize), svidWithFlagArray, cn0Array, elevArray, azimArray,
- carrierFreqArray);
+ carrierFreqArray, basebandCn0Array);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -1545,7 +1550,7 @@
method_reportLocation = env->GetMethodID(clazz, "reportLocation",
"(ZLandroid/location/Location;)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
- method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F)V");
+ method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V");
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(I)V");
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index efffa6c..692c9d2 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -40,7 +40,6 @@
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
-int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
int register_android_server_TestNetworkService(JNIEnv* env);
int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
@@ -88,7 +87,6 @@
register_android_server_SystemServer(env);
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
- register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
register_android_server_TestNetworkService(env);
register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c4130d9..b264684 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5574,8 +5574,8 @@
}
}
- private void enforceProfileOwnerOfCorpOwnedDevice(ActiveAdmin admin) {
- if (!isProfileOwnerOfOrganizationOwnedDevicte(admin)) {
+ private void enforceProfileOwnerOfOrganizationOwnedDevice(ActiveAdmin admin) {
+ if (!isProfileOwnerOfOrganizationOwnedDevice(admin)) {
throw new SecurityException(String.format("Provided admin %s is either not a profile "
+ "owner or not on a corporate-owned device.", admin));
}
@@ -6645,7 +6645,7 @@
}
boolean calledByProfileOwnerOnOrgOwnedDevice =
- isProfileOwnerOfOrganizationOwnedDevicte(admin);
+ isProfileOwnerOfOrganizationOwnedDevice(admin);
if (calledOnParentInstance && !calledByProfileOwnerOnOrgOwnedDevice) {
throw new SecurityException("Wiping the entire device can only be done by a profile"
@@ -6685,6 +6685,9 @@
mUserManager.setUserRestriction(
UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, false,
UserHandle.SYSTEM);
+
+ // Device-wide policies set by the profile owner need to be cleaned up here.
+ mLockPatternUtils.setDeviceOwnerInfo(null);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -8026,7 +8029,7 @@
* {@code getActiveAdminForCallerLocked} or one of the similar variants, not caller-supplied
* input.
*/
- private boolean isProfileOwnerOfOrganizationOwnedDevicte(@Nullable ActiveAdmin admin) {
+ private boolean isProfileOwnerOfOrganizationOwnedDevice(@Nullable ActiveAdmin admin) {
if (admin == null) {
return false;
}
@@ -8334,14 +8337,17 @@
}
synchronized (getLockObject()) {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- long token = mInjector.binderClearCallingIdentity();
- try {
- mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null);
- } finally {
- mInjector.binderRestoreCallingIdentity(token);
+ ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (!isProfileOwnerOfOrganizationOwnedDevice(admin) && !isDeviceOwner(admin)) {
+ throw new SecurityException("Only Device Owner or Profile Owner of"
+ + " organization-owned device can set screen lock info.");
}
}
+
+ mInjector.binderWithCleanCallingIdentity(() ->
+ mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null));
+
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.SET_DEVICE_OWNER_LOCK_SCREEN_INFO)
.setAdmin(who)
@@ -10229,8 +10235,7 @@
synchronized (getLockObject()) {
final boolean isDeviceOwner = mOwners.isDeviceOwnerUserId(userId);
final Bundle userRestrictions;
- // Whether device owner enforces camera restriction.
- boolean disallowCameraGlobally = false;
+ final int restrictionOwnerType;
if (isDeviceOwner) {
final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
@@ -10238,33 +10243,45 @@
return; // Shouldn't happen.
}
userRestrictions = deviceOwner.userRestrictions;
- // DO can disable camera globally.
- disallowCameraGlobally = deviceOwner.disableCamera;
+ addOrRemoveDisableCameraRestriction(userRestrictions, deviceOwner);
+ restrictionOwnerType = UserManagerInternal.OWNER_TYPE_DEVICE_OWNER;
} else {
final ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userId);
userRestrictions = profileOwner != null ? profileOwner.userRestrictions : null;
+ addOrRemoveDisableCameraRestriction(userRestrictions, userId);
+
+ if (isProfileOwnerOfOrganizationOwnedDevice(profileOwner)) {
+ restrictionOwnerType =
+ UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE;
+ } else if (profileOwner != null) {
+ restrictionOwnerType = UserManagerInternal.OWNER_TYPE_PROFILE_OWNER;
+ } else {
+ restrictionOwnerType = UserManagerInternal.OWNER_TYPE_NO_OWNER;
+ }
}
- // Whether any admin enforces camera restriction.
- final int cameraRestrictionScope =
- getCameraRestrictionScopeLocked(userId, disallowCameraGlobally);
-
mUserManagerInternal.setDevicePolicyUserRestrictions(userId, userRestrictions,
- isDeviceOwner, cameraRestrictionScope);
+ restrictionOwnerType);
}
}
- /**
- * Get the scope of camera restriction for a given user if any.
- */
- private int getCameraRestrictionScopeLocked(int userId, boolean disallowCameraGlobally) {
- if (disallowCameraGlobally) {
- return UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
- } else if (getCameraDisabled(
- /* who= */ null, userId, /* mergeDeviceOwnerRestriction= */ false)) {
- return UserManagerInternal.CAMERA_DISABLED_LOCALLY;
+ private void addOrRemoveDisableCameraRestriction(Bundle userRestrictions, ActiveAdmin admin) {
+ if (userRestrictions == null) return;
+ if (admin.disableCamera) {
+ userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ } else {
+ userRestrictions.remove(UserManager.DISALLOW_CAMERA);
}
- return UserManagerInternal.CAMERA_NOT_DISABLED;
+ }
+
+ private void addOrRemoveDisableCameraRestriction(Bundle userRestrictions, int userId) {
+ if (userRestrictions == null) return;
+ if (getCameraDisabled(/* who= */ null, userId, /* mergeDeviceOwnerRestriction= */
+ false)) {
+ userRestrictions.putBoolean(UserManager.DISALLOW_CAMERA, true);
+ } else {
+ userRestrictions.remove(UserManager.DISALLOW_CAMERA);
+ }
}
@Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f46857a..66f01f3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -43,6 +43,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
+import android.net.TetheringManager;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -268,6 +269,8 @@
"com.android.internal.car.CarServiceHelperService";
private static final String TIME_DETECTOR_SERVICE_CLASS =
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
+ private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
+ "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
@@ -1111,9 +1114,10 @@
SignedConfigService.registerUpdateReceiver(mSystemContext);
t.traceEnd();
- } catch (RuntimeException e) {
+ } catch (Throwable e) {
Slog.e("System", "******************************************");
- Slog.e("System", "************ Failure starting core service", e);
+ Slog.e("System", "************ Failure starting core service");
+ throw e;
}
// Before things start rolling, be sure we have decided whether
@@ -1480,6 +1484,14 @@
}
t.traceEnd();
+ t.traceBegin("StartTimeZoneDetectorService");
+ try {
+ mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
+ } catch (Throwable e) {
+ reportWtf("starting StartTimeZoneDetectorService service", e);
+ }
+ t.traceEnd();
+
if (!isWatch) {
t.traceBegin("StartSearchManagerService");
try {
@@ -2204,6 +2216,15 @@
}
t.traceEnd();
+ t.traceBegin("StartTethering");
+ try {
+ // Tethering must start after ConnectivityService and NetworkStack.
+ TetheringManager.getInstance().start();
+ } catch (Throwable e) {
+ reportWtf("starting Tethering", e);
+ }
+ t.traceEnd();
+
t.traceBegin("MakeCountryDetectionServiceReady");
try {
if (countryDetectorF != null) {
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 7ef9f44..3babb0b 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -9,8 +9,8 @@
name: "services.net",
srcs: [
":net-module-utils-srcs",
- ":tethering-servicesnet-srcs",
":services.net-sources",
+ ":tethering-manager",
],
static_libs: [
"dnsresolver_aidl_interface-V2-java",
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 2cebbeb..223a98b 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -29,6 +29,9 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.Map;
+import java.util.Set;
+
@Implements(PerformUnifiedRestoreTask.class)
public class ShadowPerformUnifiedRestoreTask {
@Nullable private static ShadowPerformUnifiedRestoreTask sLastShadow;
@@ -64,7 +67,8 @@
int pmToken,
boolean isFullSystemRestore,
@Nullable String[] filterSet,
- OnTaskFinishedListener listener) {
+ OnTaskFinishedListener listener,
+ Map<String, Set<String>> excludedKeys) {
mBackupManagerService = backupManagerService;
mPackage = targetPackage;
mIsFullSystemRestore = isFullSystemRestore;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 0be5fe0..cf7919b 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -41,6 +41,7 @@
"hamcrest-library",
"servicestests-utils",
"xml-writer-device-lib",
+ "service-appsearch",
"service-jobscheduler",
],
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
new file mode 100644
index 0000000..adef02e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.appsearch.impl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.android.icing.proto.DocumentProto;
+import com.google.android.icing.proto.PropertyProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class FakeIcingTest {
+ @Test
+ public void query() {
+ FakeIcing icing = new FakeIcing();
+ icing.put(createDoc("uri:cat", "The cat said meow"));
+ icing.put(createDoc("uri:dog", "The dog said woof"));
+
+ assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+ assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+ assertThat(queryGetUris(icing, "fred")).isEmpty();
+ }
+
+ @Test
+ public void queryNorm() {
+ FakeIcing icing = new FakeIcing();
+ icing.put(createDoc("uri:cat", "The cat said meow"));
+ icing.put(createDoc("uri:dog", "The dog said woof"));
+
+ assertThat(queryGetUris(icing, "the")).containsExactly("uri:cat", "uri:dog");
+ assertThat(queryGetUris(icing, "The")).containsExactly("uri:cat", "uri:dog");
+ assertThat(queryGetUris(icing, "tHe")).containsExactly("uri:cat", "uri:dog");
+ }
+
+ @Test
+ public void get() {
+ DocumentProto cat = createDoc("uri:cat", "The cat said meow");
+ FakeIcing icing = new FakeIcing();
+ icing.put(cat);
+ assertThat(icing.get("uri:cat")).isEqualTo(cat);
+ }
+
+ @Test
+ public void replace() {
+ DocumentProto cat = createDoc("uri:cat", "The cat said meow");
+ DocumentProto dog = createDoc("uri:dog", "The dog said woof");
+
+ FakeIcing icing = new FakeIcing();
+ icing.put(cat);
+ icing.put(dog);
+
+ assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+ assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+ assertThat(icing.get("uri:cat")).isEqualTo(cat);
+
+ // Replace
+ DocumentProto cat2 = createDoc("uri:cat", "The cat said purr");
+ DocumentProto bird = createDoc("uri:bird", "The cat said tweet");
+ icing.put(cat2);
+ icing.put(bird);
+
+ assertThat(queryGetUris(icing, "meow")).isEmpty();
+ assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog", "uri:bird");
+ assertThat(icing.get("uri:cat")).isEqualTo(cat2);
+ }
+
+ @Test
+ public void delete() {
+ DocumentProto cat = createDoc("uri:cat", "The cat said meow");
+ DocumentProto dog = createDoc("uri:dog", "The dog said woof");
+
+ FakeIcing icing = new FakeIcing();
+ icing.put(cat);
+ icing.put(dog);
+
+ assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
+ assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
+ assertThat(icing.get("uri:cat")).isEqualTo(cat);
+
+ // Delete
+ icing.delete("uri:cat");
+ icing.delete("uri:notreal");
+
+ assertThat(queryGetUris(icing, "meow")).isEmpty();
+ assertThat(queryGetUris(icing, "said")).containsExactly("uri:dog");
+ assertThat(icing.get("uri:cat")).isNull();
+ }
+
+ private static DocumentProto createDoc(String uri, String body) {
+ return DocumentProto.newBuilder()
+ .setUri(uri)
+ .addProperties(PropertyProto.newBuilder().addStringValues(body))
+ .build();
+ }
+
+ private static List<String> queryGetUris(FakeIcing icing, String term) {
+ List<String> uris = new ArrayList<>();
+ for (DocumentProto result : icing.query(term)) {
+ uris.add(result.getUri());
+ }
+ return uris;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
index f588c4f..4e7fe44 100644
--- a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -18,6 +18,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -50,6 +53,7 @@
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
@Mock private Consumer<String> mConsumer;
+ @Mock private File invalidFile;
private File mFile;
private DataChangedJournal mJournal;
@@ -131,4 +135,10 @@
public void toString_isSameAsFileToString() throws Exception {
assertThat(mJournal.toString()).isEqualTo(mFile.toString());
}
+
+ public void listJournals_invalidJournalFile_returnsEmptyList() throws Exception {
+ when(invalidFile.listFiles()).thenReturn(null);
+
+ assertEquals(0, DataChangedJournal.listJournals(invalidFile).size());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
new file mode 100644
index 0000000..d6efe35
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.backup;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class UserBackupPreferencesTest {
+ private static final String EXCLUDED_PACKAGE_1 = "package1";
+ private static final String EXCLUDED_PACKAGE_2 = "package2";
+ private static final List<String> EXCLUDED_KEYS_1 = Arrays.asList("key1", "key2");
+ private static final List<String> EXCLUDED_KEYS_2 = Arrays.asList("key1");
+
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ private UserBackupPreferences mExcludedRestoreKeysStorage;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mExcludedRestoreKeysStorage =
+ new UserBackupPreferences(
+ InstrumentationRegistry.getContext(), mTemporaryFolder.newFolder());
+ }
+
+ @Test
+ public void testGetExcludedKeysForPackages_returnsExcludedKeys() {
+ mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_1, EXCLUDED_KEYS_1);
+ mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_2, EXCLUDED_KEYS_2);
+
+ Map<String, Set<String>> excludedKeys =
+ mExcludedRestoreKeysStorage.getExcludedRestoreKeysForPackages(EXCLUDED_PACKAGE_1);
+ assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_1));
+ assertFalse(excludedKeys.containsKey(EXCLUDED_PACKAGE_2));
+ assertEquals(new HashSet<>(EXCLUDED_KEYS_1), excludedKeys.get(EXCLUDED_PACKAGE_1));
+ }
+
+ @Test
+ public void testGetExcludedKeysForPackages_withEmpty_list_returnsAllExcludedKeys() {
+ mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_1, EXCLUDED_KEYS_1);
+ mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_2, EXCLUDED_KEYS_2);
+
+ Map<String, Set<String>> excludedKeys =
+ mExcludedRestoreKeysStorage.getAllExcludedRestoreKeys();
+ assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_1));
+ assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_2));
+ assertEquals(new HashSet<>(EXCLUDED_KEYS_1), excludedKeys.get(EXCLUDED_PACKAGE_1));
+ assertEquals(new HashSet<>(EXCLUDED_KEYS_2), excludedKeys.get(EXCLUDED_PACKAGE_2));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
new file mode 100644
index 0000000..6359edf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.backup.restore;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayDeque;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class PerformUnifiedRestoreTaskTest {
+ private static final String PACKAGE_NAME = "package";
+ private static final String INCLUDED_KEY = "included_key";
+ private static final String EXCLUDED_KEY_1 = "excluded_key_1";
+ private static final String EXCLUDED_KEY_2 = "excluded_key_2";
+
+ @Mock private BackupDataInput mBackupDataInput;
+ @Mock private BackupDataOutput mBackupDataOutput;
+
+ private Set<String> mExcludedkeys = new HashSet<>();
+ private Map<String, String> mBackupData = new HashMap<>();
+ // Mock BackupDataInput reads backup data from here.
+ private Queue<String> mBackupDataSource;
+ // Mock BackupDataOutput will write backup data here.
+ private Set<String> mBackupDataDump;
+ private PerformUnifiedRestoreTask mRestoreTask;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ populateTestData();
+
+ mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
+ when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return !mBackupDataSource.isEmpty();
+ }
+ });
+ when(mBackupDataInput.getKey()).then(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ return mBackupDataSource.poll();
+ }
+ });
+ when(mBackupDataInput.getDataSize()).thenReturn(0);
+
+ mBackupDataDump = new HashSet<>();
+ ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class);
+ when(mBackupDataOutput.writeEntityHeader(keyCaptor.capture(), anyInt())).then(
+ new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mBackupDataDump.add(keyCaptor.getValue());
+ return null;
+ }
+ });
+
+ mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
+ PACKAGE_NAME, mExcludedkeys));
+ }
+
+ private void populateTestData() {
+ mBackupData = new HashMap<>();
+ mBackupData.put(INCLUDED_KEY, "1");
+ mBackupData.put(EXCLUDED_KEY_1, "2");
+ mBackupData.put(EXCLUDED_KEY_2, "3");
+
+ mExcludedkeys = new HashSet<>();
+ mExcludedkeys.add(EXCLUDED_KEY_1);
+ mExcludedkeys.add(EXCLUDED_KEY_2);
+ }
+
+ @Test
+ public void testFilterExcludedKeys() throws Exception {
+ mRestoreTask.filterExcludedKeys(PACKAGE_NAME, mBackupDataInput, mBackupDataOutput);
+
+ // Verify only the correct were written into BackupDataOutput object.
+ Set<String> allowedBackupKeys = new HashSet<>(mBackupData.keySet());
+ allowedBackupKeys.removeAll(mExcludedkeys);
+ assertEquals(allowedBackupKeys, mBackupDataDump);
+ }
+}
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 b6ed22b..162e766 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -26,9 +26,6 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPassword;
-import static android.os.UserManagerInternal.CAMERA_DISABLED_GLOBALLY;
-import static android.os.UserManagerInternal.CAMERA_DISABLED_LOCALLY;
-import static android.os.UserManagerInternal.CAMERA_NOT_DISABLED;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -83,6 +80,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManagerInternal;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.security.KeyChain;
@@ -1155,9 +1153,8 @@
MockUtils.checkUserHandle(UserHandle.USER_SYSTEM));
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
- eq(UserHandle.USER_SYSTEM),
- eq(null),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserHandle.USER_SYSTEM), eq(null),
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
null, UserHandle.USER_SYSTEM);
@@ -1726,8 +1723,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(defaultRestrictions),
- eq(true) /* isDeviceOwner */,
- eq(CAMERA_NOT_DISABLED)
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER)
);
reset(getServices().userManagerInternal);
@@ -1742,7 +1738,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -1750,7 +1746,7 @@
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS,
UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
DpmTestUtils.assertRestrictions(
@@ -1768,7 +1764,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
DpmTestUtils.assertRestrictions(
@@ -1784,7 +1780,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
assertNoDeviceOwnerRestrictions();
@@ -1798,7 +1794,7 @@
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_UNMUTE_MICROPHONE),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
dpm.clearUserRestriction(admin1, UserManager.DISALLOW_ADJUST_VOLUME);
@@ -1810,7 +1806,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_FUN);
@@ -1818,16 +1814,16 @@
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
dpm.setCameraDisabled(admin1, true);
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
- // DISALLOW_CAMERA will be applied to both local and global.
+ // DISALLOW_CAMERA will be applied globally.
MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
- UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_DISABLED_GLOBALLY));
+ UserManager.DISALLOW_ADD_USER, UserManager.DISALLOW_CAMERA),
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
reset(getServices().userManagerInternal);
}
@@ -1873,7 +1869,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES),
- eq(false), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
@@ -1881,7 +1877,7 @@
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
UserManager.DISALLOW_OUTGOING_CALLS),
- eq(false), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
DpmTestUtils.assertRestrictions(
@@ -1904,7 +1900,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_OUTGOING_CALLS),
- eq(false), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
DpmTestUtils.assertRestrictions(
@@ -1925,7 +1921,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(),
- eq(false), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
DpmTestUtils.assertRestrictions(
@@ -1947,20 +1943,52 @@
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_UNMUTE_MICROPHONE),
- eq(false), eq(CAMERA_NOT_DISABLED));
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
dpm.setCameraDisabled(admin1, true);
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(DpmMockContext.CALLER_USER_HANDLE),
MockUtils.checkUserRestrictions(UserManager.DISALLOW_ADJUST_VOLUME,
- UserManager.DISALLOW_UNMUTE_MICROPHONE),
- eq(false), eq(CAMERA_DISABLED_LOCALLY));
+ UserManager.DISALLOW_UNMUTE_MICROPHONE, UserManager.DISALLOW_CAMERA),
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER));
reset(getServices().userManagerInternal);
// TODO Make sure restrictions are written to the file.
}
+ public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
+ setupProfileOwner();
+
+ final long ident = mServiceContext.binder.clearCallingIdentity();
+ configureContextForAccess(mServiceContext, true);
+
+ mServiceContext.binder.callingUid =
+ UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE,
+ DpmMockContext.CALLER_MANAGED_PROVISIONING_UID);
+ try {
+ runAsCaller(mServiceContext, dpms, dpm -> {
+ dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1);
+ });
+ } finally {
+ mServiceContext.binder.restoreCallingIdentity(ident);
+ }
+
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_CONFIG_DATE_TIME);
+ verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME),
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+ reset(getServices().userManagerInternal);
+
+ dpm.setCameraDisabled(admin1, true);
+ verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
+ eq(DpmMockContext.CALLER_USER_HANDLE),
+ MockUtils.checkUserRestrictions(UserManager.DISALLOW_CONFIG_DATE_TIME,
+ UserManager.DISALLOW_CAMERA),
+ eq(UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE));
+ reset(getServices().userManagerInternal);
+ }
public void testDefaultEnabledUserRestrictions() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
@@ -1995,8 +2023,7 @@
verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
eq(UserHandle.USER_SYSTEM),
MockUtils.checkUserRestrictions(defaultRestrictions),
- eq(true) /* isDeviceOwner */,
- eq(CAMERA_NOT_DISABLED)
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER)
);
reset(getServices().userManagerInternal);
@@ -2036,10 +2063,9 @@
dpm.getUserRestrictions(admin1)
);
verify(getServices().userManagerInternal, atLeast(1)).setDevicePolicyUserRestrictions(
- eq(UserHandle.USER_SYSTEM),
- MockUtils.checkUserRestrictions(newDefaultEnabledRestriction),
- eq(true) /* isDeviceOwner */,
- eq(CAMERA_NOT_DISABLED)
+ eq(UserHandle.USER_SYSTEM),
+ MockUtils.checkUserRestrictions(newDefaultEnabledRestriction),
+ eq(UserManagerInternal.OWNER_TYPE_DEVICE_OWNER)
);
reset(getServices().userManagerInternal);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
new file mode 100644
index 0000000..269f918
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DisplayModeDirectorTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange(
+ int minFps, int maxFps) {
+ DisplayModeDirector director =
+ new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+ int displayId = 0;
+ int numModes = maxFps - minFps + 1;
+ Display.Mode[] modes = new Display.Mode[numModes];
+ for (int i = minFps; i <= maxFps; i++) {
+ modes[i - minFps] = new Display.Mode(
+ /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i);
+ }
+ SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>();
+ supportedModesByDisplay.put(displayId, modes);
+ director.injectSupportedModesByDisplay(supportedModesByDisplay);
+ SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>();
+ defaultModesByDisplay.put(displayId, modes[0]);
+ director.injectDefaultModeByDisplay(defaultModesByDisplay);
+ return director;
+ }
+
+ private int[] intRange(int min, int max) {
+ int[] range = new int[max - min + 1];
+ for (int i = min; i <= max; i++) {
+ range[i - min] = i;
+ }
+ return range;
+ }
+
+ @Test
+ public void testDisplayModeVoting() {
+ int displayId = 0;
+
+ // With no votes present, DisplayModeDirector should allow any refresh rate.
+ assertEquals(new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/60,
+ new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY),
+ intRange(60, 90)),
+ createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayConfigSpecs(
+ displayId));
+
+ int numPriorities =
+ DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1;
+
+ // Ensure vote priority works as expected. As we add new votes with higher priority, they
+ // should take precedence over lower priority votes.
+ {
+ int minFps = 60;
+ int maxFps = 90;
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+ assertTrue(2 * numPriorities < maxFps - minFps + 1);
+ SparseArray<DisplayModeDirector.Vote> votes =
+ new SparseArray<DisplayModeDirector.Vote>();
+ SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
+ new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+ votesByDisplay.put(displayId, votes);
+ for (int i = 0; i < numPriorities; i++) {
+ int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i;
+ votes.put(
+ priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i));
+ director.injectVotesByDisplay(votesByDisplay);
+ assertEquals(
+ new DisplayModeDirector.DesiredDisplayConfigSpecs(
+ /*defaultModeId=*/minFps + i,
+ new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i),
+ intRange(minFps + i, maxFps - i)),
+ director.getDesiredDisplayConfigSpecs(displayId));
+ }
+ }
+
+ // Ensure lower priority votes are able to influence the final decision, even in the
+ // presence of higher priority votes.
+ {
+ assertTrue(numPriorities >= 2);
+ DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90);
+ SparseArray<DisplayModeDirector.Vote> votes =
+ new SparseArray<DisplayModeDirector.Vote>();
+ SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay =
+ new SparseArray<SparseArray<DisplayModeDirector.Vote>>();
+ votesByDisplay.put(displayId, votes);
+ votes.put(DisplayModeDirector.Vote.MAX_PRIORITY,
+ DisplayModeDirector.Vote.forRefreshRates(65, 85));
+ votes.put(DisplayModeDirector.Vote.MIN_PRIORITY,
+ DisplayModeDirector.Vote.forRefreshRates(70, 80));
+ director.injectVotesByDisplay(votesByDisplay);
+ assertEquals(
+ new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/70,
+ new DisplayModeDirector.RefreshRateRange(70, 80), intRange(70, 80)),
+ director.getDesiredDisplayConfigSpecs(displayId));
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
index 495923d..a14197b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleXmlParserTest.java
@@ -30,6 +30,7 @@
import java.io.ByteArrayInputStream;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -109,7 +110,7 @@
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -154,7 +155,7 @@
"test_cert",
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -200,7 +201,7 @@
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -237,7 +238,7 @@
/* isHashedValue= */ false))),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -273,7 +274,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
- () -> xmlParser.parse(ruleXmlCompoundFormula));
+ () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -302,7 +303,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "For input string: \"INVALID_OPERATOR\"",
- () -> xmlParser.parse(ruleXmlCompoundFormula));
+ () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -330,7 +331,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "For input string: \"INVALID_EFFECT\"",
- () -> xmlParser.parse(ruleXmlCompoundFormula));
+ () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -360,7 +361,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Found unexpected tag: InvalidAtomicFormula",
- () -> xmlParser.parse(ruleXmlCompoundFormula));
+ () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -387,7 +388,7 @@
/* isHashedValue= */ false),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -415,7 +416,7 @@
AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -441,7 +442,7 @@
new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -470,7 +471,7 @@
/* isHashedValue= */ false),
Rule.DENY);
- List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula);
+ List<Rule> rules = xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8));
assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
}
@@ -495,7 +496,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Found unexpected key: -1",
- () -> xmlParser.parse(ruleXmlAtomicFormula));
+ () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -517,7 +518,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Unknown effect: -1",
- () -> xmlParser.parse(ruleXmlAtomicFormula));
+ () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -545,7 +546,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Unknown connector: -1",
- () -> xmlParser.parse(ruleXmlCompoundFormula));
+ () -> xmlParser.parse(ruleXmlCompoundFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -569,7 +570,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "For input string: \"com.app.test\"",
- () -> xmlParser.parse(ruleXmlAtomicFormula));
+ () -> xmlParser.parse(ruleXmlAtomicFormula.getBytes(StandardCharsets.UTF_8)));
}
@Test
@@ -595,7 +596,7 @@
assertExpectException(
RuleParseException.class,
/* expectedExceptionMessageRegex */ "Rules must start with RuleList <RL> tag",
- () -> xmlParser.parse(ruleXmlWithNoRuleList));
+ () -> xmlParser.parse(ruleXmlWithNoRuleList.getBytes(StandardCharsets.UTF_8)));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
index 40e89ba..180de2f 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
@@ -38,6 +38,7 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Optional;
@RunWith(JUnit4.class)
public class RuleXmlSerializerTest {
@@ -48,7 +49,9 @@
RuleSerializer xmlSerializer = new RuleXmlSerializer();
String expectedRules = "<RL />";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@@ -56,53 +59,74 @@
@Test
public void testXmlString_serializeMultipleRules_oneEmpty() throws Exception {
Rule rule1 = null;
- Rule rule2 = new Rule(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test",
- /* isHashedValue= */ false),
- Rule.DENY);
+ Rule rule2 =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
packageNameAttrs.put("V", "com.app.test");
packageNameAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Arrays.asList(rule1, rule2));
+ String actualRules =
+ xmlSerializer.serialize(
+ Arrays.asList(rule1, rule2), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlStream_serializeValidOpenFormula() throws Exception {
- Rule rule = new Rule(new CompoundFormula(CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- "com.app.test", /* isHashedValue= */ false))), Rule.DENY);
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false))),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
OutputStream outputStream = new ByteArrayOutputStream();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
packageNameAttrs.put("V", "com.app.test");
packageNameAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + "</OF>"
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + "</OF>"
+ + "</R>"
+ + "</RL>";
- xmlSerializer.serialize(Collections.singletonList(rule), outputStream);
+ xmlSerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty(),
+ outputStream);
String actualRules = outputStream.toString();
assertEquals(expectedRules, actualRules);
@@ -110,39 +134,60 @@
@Test
public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception {
- Rule rule = new Rule(new CompoundFormula(CompoundFormula.NOT,
- Collections.singletonList(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- "com.app.test", /* isHashedValue= */ false))), Rule.DENY);
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false))),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
packageNameAttrs.put("V", "com.app.test");
packageNameAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + "</OF>"
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.NOT)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + "</OF>"
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception {
- Rule rule = new Rule(new CompoundFormula(CompoundFormula.AND,
- Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- "com.app.test", /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE,
- "test_cert", /* isHashedValue= */ false))), Rule.DENY);
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ "test_cert",
+ /* isHashedValue= */ false))),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
@@ -152,31 +197,47 @@
appCertificateAttrs.put("K", String.valueOf(AtomicFormula.APP_CERTIFICATE));
appCertificateAttrs.put("V", "test_cert");
appCertificateAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
- + "</OF>"
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.AND)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ + "</OF>"
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception {
- Rule rule = new Rule(new CompoundFormula(CompoundFormula.OR,
- Arrays.asList(new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME,
- "com.app.test", /* isHashedValue= */ false),
- new AtomicFormula.StringAtomicFormula(AtomicFormula.APP_CERTIFICATE,
- "test_cert", /* isHashedValue= */ false))), Rule.DENY);
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.OR,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ "test_cert",
+ /* isHashedValue= */ false))),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
@@ -186,89 +247,117 @@
appCertificateAttrs.put("K", String.valueOf(AtomicFormula.APP_CERTIFICATE));
appCertificateAttrs.put("V", "test_cert");
appCertificateAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "OF",
- Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + generateTagWithAttribute(/* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
- + "</OF>"
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "OF",
+ Collections.singletonMap("C", String.valueOf(CompoundFormula.OR)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", appCertificateAttrs, /* closed= */ true)
+ + "</OF>"
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlString_serializeValidAtomicFormula_stringValue() throws Exception {
- Rule rule = new Rule(
- new AtomicFormula.StringAtomicFormula(AtomicFormula.PACKAGE_NAME, "com.app.test",
- /* isHashedValue= */ false),
- Rule.DENY);
+ Rule rule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ "com.app.test",
+ /* isHashedValue= */ false),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> packageNameAttrs = new LinkedHashMap<>();
packageNameAttrs.put("K", String.valueOf(AtomicFormula.PACKAGE_NAME));
packageNameAttrs.put("V", "com.app.test");
packageNameAttrs.put("H", "false");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", packageNameAttrs, /* closed= */ true)
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", packageNameAttrs, /* closed= */ true)
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlString_serializeValidAtomicFormula_integerValue() throws Exception {
- Rule rule = new Rule(
- new AtomicFormula.IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
- Rule.DENY);
+ Rule rule =
+ new Rule(
+ new AtomicFormula.IntAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> versionCodeAttrs = new LinkedHashMap<>();
versionCodeAttrs.put("K", String.valueOf(AtomicFormula.VERSION_CODE));
versionCodeAttrs.put("O", String.valueOf(AtomicFormula.EQ));
versionCodeAttrs.put("V", "1");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", versionCodeAttrs, /* closed= */ true)
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@Test
public void testXmlString_serializeValidAtomicFormula_booleanValue() throws Exception {
- Rule rule = new Rule(
- new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
- Rule.DENY);
+ Rule rule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
RuleSerializer xmlSerializer = new RuleXmlSerializer();
Map<String, String> preInstalledAttrs = new LinkedHashMap<>();
preInstalledAttrs.put("K", String.valueOf(AtomicFormula.PRE_INSTALLED));
preInstalledAttrs.put("V", "true");
- String expectedRules = "<RL>"
- + generateTagWithAttribute(/* tag= */ "R",
- Collections.singletonMap("E", String.valueOf(Rule.DENY)),
- /* closed= */ false)
- + generateTagWithAttribute(/* tag= */ "AF", preInstalledAttrs, /* closed= */ true)
- + "</R>"
- + "</RL>";
+ String expectedRules =
+ "<RL>"
+ + generateTagWithAttribute(
+ /* tag= */ "R",
+ Collections.singletonMap("E", String.valueOf(Rule.DENY)),
+ /* closed= */ false)
+ + generateTagWithAttribute(
+ /* tag= */ "AF", preInstalledAttrs, /* closed= */ true)
+ + "</R>"
+ + "</RL>";
- String actualRules = xmlSerializer.serialize(Collections.singletonList(rule));
+ String actualRules =
+ xmlSerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
assertEquals(expectedRules, actualRules);
}
@@ -282,11 +371,14 @@
assertExpectException(
RuleSerializeException.class,
/* expectedExceptionMessageRegex */ "Invalid formula type",
- () -> xmlSerializer.serialize(Collections.singletonList(rule)));
+ () ->
+ xmlSerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty()));
}
- private String generateTagWithAttribute(String tag, Map<String, String> attributeValues,
- boolean closed) {
+ private String generateTagWithAttribute(
+ String tag, Map<String, String> attributeValues, boolean closed) {
StringBuilder res = new StringBuilder("<");
res.append(tag);
for (String attribute : attributeValues.keySet()) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
new file mode 100644
index 0000000..4b7dd36
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.pm;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.PackageParser;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.compat.PlatformCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+
+/**
+ * {@link SELinuxMMAC} tests.
+ */
+@RunWith(MockitoJUnitRunner.class)
+@Presubmit
+public class SELinuxMMACTest {
+
+ private static final String PACKAGE_NAME = "my.package";
+ private static final int OPT_IN_VERSION = android.os.Build.VERSION_CODES.R;
+
+ @Mock
+ PlatformCompat mMockCompatibility;
+
+ PackageParser.Package mPkg;
+
+ @Before
+ public void setUp() {
+ mPkg = new PackageParser.Package(PACKAGE_NAME);
+ mPkg.applicationInfo.targetSdkVersion = 28;
+ }
+
+ @Test
+ public void getSeInfoOptInToLatest() {
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoNoOptIn() {
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=28"));
+ }
+
+ @Test
+ public void getSeInfoNoOptInButAlreadyR() {
+ mPkg.applicationInfo.targetSdkVersion = OPT_IN_VERSION;
+ when(mMockCompatibility.isChangeEnabled(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ mPkg.applicationInfo)).thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(mPkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 3ea3b3c..74ef034 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -48,6 +48,8 @@
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
+import com.android.server.compat.PlatformCompat;
+
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -71,12 +73,15 @@
@Mock
UserManagerInternal mMockUserManager;
@Mock
+ PlatformCompat mMockCompatibility;
+ @Mock
PackageManagerService.Injector mMockInjector;
@Before
public void setupInjector() {
when(mMockInjector.getAbiHelper()).thenReturn(mMockPackageAbiHelper);
when(mMockInjector.getUserManagerInternal()).thenReturn(mMockUserManager);
+ when(mMockInjector.getCompatibility()).thenReturn(mMockCompatibility);
}
@Before
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 2cc5323..1a630ff 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -122,13 +122,15 @@
final Bundle local = new Bundle();
final Bundle global = new Bundle();
- UserRestrictionsUtils.sortToGlobalAndLocal(null, false /* isDeviceOwner */,
- UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+ UserRestrictionsUtils.sortToGlobalAndLocal(null,
+ UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
+ global, local);
assertEquals(0, global.size());
assertEquals(0, local.size());
- UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false /* isDeviceOwner */,
- UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+ UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY,
+ UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
+ global, local);
assertEquals(0, global.size());
assertEquals(0, local.size());
@@ -140,8 +142,10 @@
UserManager.DISALLOW_CONFIG_TETHERING,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_APPS_CONTROL,
- UserManager.ENSURE_VERIFY_APPS
- ), true /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+ UserManager.ENSURE_VERIFY_APPS,
+ UserManager.DISALLOW_CAMERA
+ ), UserManagerInternal.OWNER_TYPE_DEVICE_OWNER,
+ global, local);
assertRestrictions(newRestrictions(
@@ -154,7 +158,10 @@
// These can only be set by DO.
UserManager.DISALLOW_USB_FILE_TRANSFER,
- UserManager.DISALLOW_CONFIG_TETHERING
+ UserManager.DISALLOW_CONFIG_TETHERING,
+
+ // This can be set by DO or PO of organisation owned device
+ UserManager.DISALLOW_CAMERA
), global);
assertRestrictions(newRestrictions(
@@ -174,8 +181,10 @@
UserManager.DISALLOW_CONFIG_TETHERING,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_APPS_CONTROL,
- UserManager.ENSURE_VERIFY_APPS
- ), false /* isDeviceOwner */, UserManagerInternal.CAMERA_NOT_DISABLED, global, local);
+ UserManager.ENSURE_VERIFY_APPS,
+ UserManager.DISALLOW_CAMERA
+ ), UserManagerInternal.OWNER_TYPE_PROFILE_OWNER,
+ global, local);
assertRestrictions(newRestrictions(
// This one is global no matter who sets it.
@@ -193,23 +202,47 @@
// These can only be set by DO.
UserManager.DISALLOW_USB_FILE_TRANSFER,
- UserManager.DISALLOW_CONFIG_TETHERING
+ UserManager.DISALLOW_CONFIG_TETHERING,
+
+ // This can be set by DO or PO of organisation owned device
+ UserManager.DISALLOW_CAMERA
), local);
+ local.clear();
+ global.clear();
+
+ // Restrictions set by PO of organisation owned device
+ UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(
+ UserManager.DISALLOW_CONFIG_DATE_TIME
+ ), UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE,
+ global, local);
+
+ assertRestrictions(newRestrictions(
+ // This user restriction is global when set by PO of org owned device
+ UserManager.DISALLOW_CONFIG_DATE_TIME
+ ), global);
+ assertEquals(0, local.size());
}
public void testSortToLocalAndGlobalWithCameraDisabled() {
final Bundle local = new Bundle();
final Bundle global = new Bundle();
- UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
- UserManagerInternal.CAMERA_DISABLED_GLOBALLY, global, local);
+ UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
+ UserManagerInternal.OWNER_TYPE_DEVICE_OWNER, global, local);
assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
assertEquals(0, local.size());
global.clear();
- UserRestrictionsUtils.sortToGlobalAndLocal(Bundle.EMPTY, false,
- UserManagerInternal.CAMERA_DISABLED_LOCALLY, global, local);
+ UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
+ UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE, global,
+ local);
+ assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), global);
+ assertEquals(0, local.size());
+ global.clear();
+
+ UserRestrictionsUtils.sortToGlobalAndLocal(newRestrictions(UserManager.DISALLOW_CAMERA),
+ UserManagerInternal.OWNER_TYPE_PROFILE_OWNER, global, local);
assertEquals(0, global.size());
assertRestrictions(newRestrictions(UserManager.DISALLOW_CAMERA), local);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
index 601e2e9..790f2b4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserSystemPackageInstallerTest.java
@@ -24,6 +24,7 @@
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DEVICE_DEFAULT;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_DISABLE;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE;
+import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST;
import static com.android.server.pm.UserSystemPackageInstaller.USER_TYPE_PACKAGE_WHITELIST_MODE_LOG;
@@ -394,33 +395,45 @@
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_LOG);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertFalse(mUserSystemPackageInstaller.isEnforceMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
+
+ setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IGNORE_OTA);
+ assertFalse(mUserSystemPackageInstaller.isLogMode());
+ assertFalse(mUserSystemPackageInstaller.isEnforceMode());
+ assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertTrue(mUserSystemPackageInstaller.isIgnoreOtaMode());
setUserTypePackageWhitelistMode(
USER_TYPE_PACKAGE_WHITELIST_MODE_LOG | USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertTrue(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertFalse(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
setUserTypePackageWhitelistMode(USER_TYPE_PACKAGE_WHITELIST_MODE_IMPLICIT_WHITELIST
| USER_TYPE_PACKAGE_WHITELIST_MODE_ENFORCE);
assertFalse(mUserSystemPackageInstaller.isLogMode());
assertTrue(mUserSystemPackageInstaller.isEnforceMode());
assertTrue(mUserSystemPackageInstaller.isImplicitWhitelistMode());
+ assertFalse(mUserSystemPackageInstaller.isIgnoreOtaMode());
}
/** Sets the whitelist mode to the desired value via adb's setprop. */
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
new file mode 100644
index 0000000..f9f23c3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyTest.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright 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.timezonedetector;
+
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
+import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
+
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGH;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_HIGHEST;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_LOW;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_MEDIUM;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_NONE;
+import static com.android.server.timezonedetector.TimeZoneDetectorStrategy.SCORE_USAGE_THRESHOLD;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.timezonedetector.PhoneTimeZoneSuggestion;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType;
+import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality;
+
+import com.android.server.timezonedetector.TimeZoneDetectorStrategy.QualifiedPhoneTimeZoneSuggestion;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedList;
+
+/**
+ * White-box unit tests for {@link TimeZoneDetectorStrategy}.
+ */
+public class TimeZoneDetectorStrategyTest {
+
+ /** A time zone used for initialization that does not occur elsewhere in tests. */
+ private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
+ private static final int PHONE1_ID = 10000;
+ private static final int PHONE2_ID = 20000;
+
+ // Suggestion test cases are ordered so that each successive one is of the same or higher score
+ // than the previous.
+ private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] {
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW),
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET,
+ SCORE_MEDIUM),
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET,
+ QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_MEDIUM),
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, SCORE_HIGH),
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH),
+ newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY,
+ QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, SCORE_HIGHEST),
+ newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, SCORE_HIGHEST),
+ };
+
+ private TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+ private FakeTimeZoneDetectorStrategyCallback mFakeTimeZoneDetectorStrategyCallback;
+
+ @Before
+ public void setUp() {
+ mFakeTimeZoneDetectorStrategyCallback = new FakeTimeZoneDetectorStrategyCallback();
+ mTimeZoneDetectorStrategy =
+ new TimeZoneDetectorStrategy(mFakeTimeZoneDetectorStrategyCallback);
+ }
+
+ @Test
+ public void testEmptySuggestions() {
+ PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion();
+ PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion();
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+ script.suggestPhoneTimeZone(phone1TimeZoneSuggestion)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, SCORE_NONE);
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertNull(mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ script.suggestPhoneTimeZone(phone2TimeZoneSuggestion)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, SCORE_NONE);
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedPhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ // Phone 1 should always beat phone 2, all other things being equal.
+ assertEquals(expectedPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+ }
+
+ @Test
+ public void testFirstPlausibleSuggestionAcceptedWhenTimeZoneUninitialized() {
+ SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
+ QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, SCORE_LOW);
+ PhoneTimeZoneSuggestion lowQualitySuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/New_York");
+
+ // The device time zone setting is left uninitialized.
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true);
+
+ // The very first suggestion will be taken.
+ script.suggestPhoneTimeZone(lowQualitySuggestion)
+ .verifyTimeZoneSetAndReset(lowQualitySuggestion);
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Another low quality suggestion will be ignored now that the setting is initialized.
+ PhoneTimeZoneSuggestion lowQualitySuggestion2 =
+ testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
+ script.suggestPhoneTimeZone(lowQualitySuggestion2)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion2 =
+ new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion2, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion2,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion2,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+ }
+
+ /**
+ * Confirms that toggling the auto time zone detection setting has the expected behavior when
+ * the strategy is "opinionated".
+ */
+ @Test
+ public void testTogglingTimeZoneDetection() {
+ Script script = new Script();
+
+ for (SuggestionTestCase testCase : TEST_CASES) {
+ // Start with the device in a known state.
+ script.initializeTimeZoneDetectionEnabled(false)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+ PhoneTimeZoneSuggestion suggestion =
+ testCase.createSuggestion(PHONE1_ID, "Europe/London");
+ script.suggestPhoneTimeZone(suggestion);
+
+ // When time zone detection is not enabled, the time zone suggestion will not be set
+ // regardless of the score.
+ script.verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(suggestion, testCase.expectedScore);
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Toggling the time zone setting on should cause the device setting to be set.
+ script.timeZoneDetectionEnabled(true);
+
+ // When time zone detection is already enabled the suggestion (if it scores highly
+ // enough) should be set immediately.
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Toggling the time zone setting should off should do nothing.
+ script.timeZoneDetectionEnabled(false)
+ .verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+ }
+ }
+
+ @Test
+ public void testSuggestionsSinglePhone() {
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
+
+ for (SuggestionTestCase testCase : TEST_CASES) {
+ makePhone1SuggestionAndCheckState(script, testCase);
+ }
+
+ /*
+ * This is the same test as above but the test cases are in
+ * reverse order of their expected score. New suggestions always replace previous ones:
+ * there's effectively no history and so ordering shouldn't make any difference.
+ */
+
+ // Each test case will have the same or lower score than the last.
+ ArrayList<SuggestionTestCase> descendingCasesByScore =
+ new ArrayList<>(Arrays.asList(TEST_CASES));
+ Collections.reverse(descendingCasesByScore);
+
+ for (SuggestionTestCase testCase : descendingCasesByScore) {
+ makePhone1SuggestionAndCheckState(script, testCase);
+ }
+ }
+
+ private void makePhone1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) {
+ // Give the next suggestion a different zone from the currently set device time zone;
+ String currentZoneId = mFakeTimeZoneDetectorStrategyCallback.getDeviceTimeZone();
+ String suggestionZoneId =
+ "Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London";
+ PhoneTimeZoneSuggestion zonePhone1Suggestion =
+ testCase.createSuggestion(PHONE1_ID, suggestionZoneId);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore);
+
+ script.suggestPhoneTimeZone(zonePhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+ }
+
+ /**
+ * Tries a set of test cases to see if the phone with the lowest ID is given preference. This
+ * test also confirms that the time zone setting would only be set if a suggestion is of
+ * sufficient quality.
+ */
+ @Test
+ public void testMultiplePhoneSuggestionScoringAndPhoneIdBias() {
+ String[] zoneIds = { "Europe/London", "Europe/Paris" };
+ PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion();
+ PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion();
+ QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, SCORE_NONE);
+ QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, SCORE_NONE);
+
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true)
+ .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
+ // Initialize the latest suggestions as empty so we don't need to worry about nulls
+ // below for the first loop.
+ .suggestPhoneTimeZone(emptyPhone1Suggestion)
+ .suggestPhoneTimeZone(emptyPhone2Suggestion)
+ .resetState();
+
+ for (SuggestionTestCase testCase : TEST_CASES) {
+ PhoneTimeZoneSuggestion zonePhone1Suggestion =
+ testCase.createSuggestion(PHONE1_ID, zoneIds[0]);
+ PhoneTimeZoneSuggestion zonePhone2Suggestion =
+ testCase.createSuggestion(PHONE2_ID, zoneIds[1]);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion,
+ testCase.expectedScore);
+ QualifiedPhoneTimeZoneSuggestion expectedZonePhone2ScoredSuggestion =
+ new QualifiedPhoneTimeZoneSuggestion(zonePhone2Suggestion,
+ testCase.expectedScore);
+
+ // Start the test by making a suggestion for phone 1.
+ script.suggestPhoneTimeZone(zonePhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone1Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedEmptyPhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Phone 2 then makes an alternative suggestion with an identical score. Phone 1's
+ // suggestion should still "win" if it is above the required threshold.
+ script.suggestPhoneTimeZone(zonePhone2Suggestion);
+ script.verifyTimeZoneNotSet();
+
+ // Assert internal service state.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ // Phone 1 should always beat phone 2, all other things being equal.
+ assertEquals(expectedZonePhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the
+ // zoneId is different, the time zone setting should be updated if the score is high
+ // enough.
+ script.suggestPhoneTimeZone(emptyPhone1Suggestion);
+ if (testCase.expectedScore >= SCORE_USAGE_THRESHOLD) {
+ script.verifyTimeZoneSetAndReset(zonePhone2Suggestion);
+ } else {
+ script.verifyTimeZoneNotSet();
+ }
+
+ // Assert internal service state.
+ assertEquals(expectedEmptyPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ assertEquals(expectedZonePhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.findBestSuggestionForTests());
+
+ // Reset the state for the next loop.
+ script.suggestPhoneTimeZone(emptyPhone2Suggestion)
+ .verifyTimeZoneNotSet();
+ assertEquals(expectedEmptyPhone1ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID));
+ assertEquals(expectedEmptyPhone2ScoredSuggestion,
+ mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID));
+ }
+ }
+
+ /**
+ * The {@link TimeZoneDetectorStrategy.Callback} is left to detect whether changing the time
+ * zone is actually necessary. This test proves that the service doesn't assume it knows the
+ * current setting.
+ */
+ @Test
+ public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting() {
+ Script script = new Script()
+ .initializeTimeZoneDetectionEnabled(true);
+
+ SuggestionTestCase testCase =
+ newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, SCORE_HIGH);
+ PhoneTimeZoneSuggestion losAngelesSuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles");
+ PhoneTimeZoneSuggestion newYorkSuggestion =
+ testCase.createSuggestion(PHONE1_ID, "America/New_York");
+
+ // Initialization.
+ script.suggestPhoneTimeZone(losAngelesSuggestion)
+ .verifyTimeZoneSetAndReset(losAngelesSuggestion);
+ // Suggest it again - it should not be set because it is already set.
+ script.suggestPhoneTimeZone(losAngelesSuggestion)
+ .verifyTimeZoneNotSet();
+
+ // Toggling time zone detection should set the device time zone only if the current setting
+ // value is different from the most recent phone suggestion.
+ script.timeZoneDetectionEnabled(false)
+ .verifyTimeZoneNotSet()
+ .timeZoneDetectionEnabled(true)
+ .verifyTimeZoneNotSet();
+
+ // Simulate a user turning auto detection off, a new suggestion being made while auto
+ // detection is off, and the user turning it on again.
+ script.timeZoneDetectionEnabled(false)
+ .suggestPhoneTimeZone(newYorkSuggestion)
+ .verifyTimeZoneNotSet();
+ // Latest suggestion should be used.
+ script.timeZoneDetectionEnabled(true)
+ .verifyTimeZoneSetAndReset(newYorkSuggestion);
+ }
+
+ private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() {
+ return new PhoneTimeZoneSuggestion.Builder(PHONE1_ID).build();
+ }
+
+ private static PhoneTimeZoneSuggestion createEmptyPhone2Suggestion() {
+ return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build();
+ }
+
+ class FakeTimeZoneDetectorStrategyCallback implements TimeZoneDetectorStrategy.Callback {
+
+ private boolean mTimeZoneDetectionEnabled;
+ private TestState<String> mTimeZoneId = new TestState<>();
+
+ @Override
+ public boolean isTimeZoneDetectionEnabled() {
+ return mTimeZoneDetectionEnabled;
+ }
+
+ @Override
+ public boolean isDeviceTimeZoneInitialized() {
+ return mTimeZoneId.getLatest() != null;
+ }
+
+ @Override
+ public String getDeviceTimeZone() {
+ return mTimeZoneId.getLatest();
+ }
+
+ @Override
+ public void setDeviceTimeZone(String zoneId) {
+ mTimeZoneId.set(zoneId);
+ }
+
+ void initializeTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ }
+
+ void initializeTimeZone(String zoneId) {
+ mTimeZoneId.init(zoneId);
+ }
+
+ void setTimeZoneDetectionEnabled(boolean enabled) {
+ mTimeZoneDetectionEnabled = enabled;
+ }
+
+ void assertTimeZoneNotSet() {
+ mTimeZoneId.assertHasNotBeenSet();
+ }
+
+ void assertTimeZoneSet(String timeZoneId) {
+ mTimeZoneId.assertHasBeenSet();
+ mTimeZoneId.assertChangeCount(1);
+ mTimeZoneId.assertLatestEquals(timeZoneId);
+ }
+
+ void commitAllChanges() {
+ mTimeZoneId.commitLatest();
+ }
+ }
+
+ /** Some piece of state that tests want to track. */
+ private static class TestState<T> {
+ private T mInitialValue;
+ private LinkedList<T> mValues = new LinkedList<>();
+
+ void init(T value) {
+ mValues.clear();
+ mInitialValue = value;
+ }
+
+ void set(T value) {
+ mValues.addFirst(value);
+ }
+
+ boolean hasBeenSet() {
+ return mValues.size() > 0;
+ }
+
+ void assertHasNotBeenSet() {
+ assertFalse(hasBeenSet());
+ }
+
+ void assertHasBeenSet() {
+ assertTrue(hasBeenSet());
+ }
+
+ void commitLatest() {
+ if (hasBeenSet()) {
+ mInitialValue = mValues.getLast();
+ mValues.clear();
+ }
+ }
+
+ void assertLatestEquals(T expected) {
+ assertEquals(expected, getLatest());
+ }
+
+ void assertChangeCount(int expectedCount) {
+ assertEquals(expectedCount, mValues.size());
+ }
+
+ public T getLatest() {
+ if (hasBeenSet()) {
+ return mValues.getFirst();
+ }
+ return mInitialValue;
+ }
+ }
+
+ /**
+ * A "fluent" class allows reuse of code in tests: initialization, simulation and verification
+ * logic.
+ */
+ private class Script {
+
+ Script initializeTimeZoneDetectionEnabled(boolean enabled) {
+ mFakeTimeZoneDetectorStrategyCallback.initializeTimeZoneDetectionEnabled(enabled);
+ return this;
+ }
+
+ Script initializeTimeZoneSetting(String zoneId) {
+ mFakeTimeZoneDetectorStrategyCallback.initializeTimeZone(zoneId);
+ return this;
+ }
+
+ Script timeZoneDetectionEnabled(boolean enabled) {
+ mFakeTimeZoneDetectorStrategyCallback.setTimeZoneDetectionEnabled(enabled);
+ mTimeZoneDetectorStrategy.handleTimeZoneDetectionChange();
+ return this;
+ }
+
+ /** Simulates the time zone detection service receiving a phone-originated suggestion. */
+ Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) {
+ mTimeZoneDetectorStrategy.suggestPhoneTimeZone(phoneTimeZoneSuggestion);
+ return this;
+ }
+
+ /** Simulates the user manually setting the time zone. */
+ Script manuallySetTimeZone(String timeZoneId) {
+ // Assert the test code is correct to call this method.
+ assertFalse(mFakeTimeZoneDetectorStrategyCallback.isTimeZoneDetectionEnabled());
+
+ mFakeTimeZoneDetectorStrategyCallback.initializeTimeZone(timeZoneId);
+ return this;
+ }
+
+ Script verifyTimeZoneNotSet() {
+ mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneNotSet();
+ return this;
+ }
+
+ Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion timeZoneSuggestion) {
+ mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(timeZoneSuggestion.getZoneId());
+ mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
+ return this;
+ }
+
+ Script resetState() {
+ mFakeTimeZoneDetectorStrategyCallback.commitAllChanges();
+ return this;
+ }
+ }
+
+ private static class SuggestionTestCase {
+ public final int matchType;
+ public final int quality;
+ public final int expectedScore;
+
+ SuggestionTestCase(int matchType, int quality, int expectedScore) {
+ this.matchType = matchType;
+ this.quality = quality;
+ this.expectedScore = expectedScore;
+ }
+
+ private PhoneTimeZoneSuggestion createSuggestion(int phoneId, String zoneId) {
+ return new PhoneTimeZoneSuggestion.Builder(phoneId)
+ .setZoneId(zoneId)
+ .setMatchType(matchType)
+ .setQuality(quality)
+ .build();
+ }
+ }
+
+ private static SuggestionTestCase newTestCase(
+ @MatchType int matchType, @Quality int quality, int expectedScore) {
+ return new SuggestionTestCase(matchType, quality, expectedScore);
+ }
+}
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 b1b386b..c2b8827 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -914,7 +914,7 @@
// Add another stack to become focused and make the activity there visible. This way it
// simulates finishing in non-focused stack in split-screen.
final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
- final ActivityRecord focusedActivity = stack.getChildAt(0).getChildAt(0);
+ final ActivityRecord focusedActivity = stack.getTopMostActivity();
focusedActivity.nowVisible = true;
focusedActivity.mVisibleRequested = true;
focusedActivity.setState(RESUMED, "test");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index ba54859..c8795ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -241,7 +241,7 @@
final RootActivityContainer.FindTaskResult result =
new RootActivityContainer.FindTaskResult();
- mStack.findTaskLocked(r, result);
+ result.process(r, mStack);
assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
@@ -266,14 +266,14 @@
final ActivityRecord r1 = new ActivityBuilder(mService).setComponent(
target).setTargetActivity(targetActivity).build();
RootActivityContainer.FindTaskResult result = new RootActivityContainer.FindTaskResult();
- mStack.findTaskLocked(r1, result);
+ result.process(r1, mStack);
assertThat(result.mRecord).isNotNull();
// Using alias activity to find task.
final ActivityRecord r2 = new ActivityBuilder(mService).setComponent(
alias).setTargetActivity(targetActivity).build();
result = new RootActivityContainer.FindTaskResult();
- mStack.findTaskLocked(r2, result);
+ result.process(r2, mStack);
assertThat(result.mRecord).isNotNull();
}
@@ -846,9 +846,9 @@
mStack.mResumedActivity = secondActivity;
// Note the activities have non-null ActivityRecord.app, so it won't remove directly.
- mStack.finishDisabledPackageActivitiesLocked(firstActivity.packageName,
- null /* filterByClasses */, true /* doit */, true /* evenPersistent */,
- UserHandle.USER_ALL);
+ mRootActivityContainer.mFinishDisabledPackageActivitiesHelper.process(
+ firstActivity.packageName, null /* filterByClasses */, true /* doit */,
+ true /* evenPersistent */, UserHandle.USER_ALL);
// If the activity is disabled with {@link android.content.pm.PackageManager#DONT_KILL_APP}
// the activity should still follow the normal flow to finish and destroy.
@@ -875,9 +875,9 @@
assertEquals(2, mTask.getChildCount());
- mStack.finishDisabledPackageActivitiesLocked(activity.packageName,
- null /* filterByClasses */, true /* doit */, true /* evenPersistent */,
- UserHandle.USER_ALL);
+ mRootActivityContainer.mFinishDisabledPackageActivitiesHelper.process(
+ activity.packageName, null /* filterByClasses */, true /* doit */,
+ true /* evenPersistent */, UserHandle.USER_ALL);
// Although the overlay activity is in another package, the non-overlay activities are
// removed from the task. Since the overlay activity should be removed as well, the task
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 4165052..d78e3af 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -167,16 +167,8 @@
@Test
public void testSuspendedPackage() {
- mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
final String suspendingPackage = "com.test.suspending.package";
- final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
- .setMessage("Test Message")
- .setIcon(0x11110001)
- .build();
- when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
- .thenReturn(suspendingPackage);
- when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
- TEST_USER_ID)).thenReturn(dialogInfo);
+ final SuspendDialogInfo dialogInfo = suspendPackage(suspendingPackage);
// THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
@@ -190,6 +182,19 @@
assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000));
}
+ private SuspendDialogInfo suspendPackage(String suspendingPackage) {
+ mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+ final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
+ .setMessage("Test Message")
+ .setIcon(0x11110001)
+ .build();
+ when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(suspendingPackage);
+ when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, suspendingPackage,
+ TEST_USER_ID)).thenReturn(dialogInfo);
+ return dialogInfo;
+ }
+
@Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode
@@ -204,6 +209,20 @@
}
@Test
+ public void testInterceptQuietProfileWhenPackageSuspended() {
+ suspendPackage("com.test.suspending.package");
+ // GIVEN that the user the activity is starting as is currently in quiet mode
+ when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+
+ // THEN calling intercept returns true
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+ // THEN the returned intent is the quiet mode intent
+ assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
+ .filterEquals(mInterceptor.mIntent));
+ }
+
+ @Test
public void testWorkChallenge() {
// GIVEN that the user the activity is starting as is currently locked
when(mAmInternal.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index ae1bb8e..c712d6d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -962,4 +962,19 @@
}
assertThat(exceptionCaught).isTrue();
}
+
+ @Test
+ public void testRecycleTaskFromAnotherUser() {
+ final ActivityStarter starter = prepareStarter(0 /* flags */);
+ starter.mStartActivity = new ActivityBuilder(mService).build();
+ final Task task = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(mService.mRootActivityContainer.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */))
+ .setUserId(10)
+ .build();
+
+ final int result = starter.recycleTask(task, null, null);
+ assertThat(result == START_SUCCESS).isTrue();
+ assertThat(starter.mAddingToTask).isTrue();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index e2bdf87..4257249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -73,6 +73,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.function.Consumer;
/**
* Tests for the {@link RootActivityContainer} class.
@@ -149,9 +150,7 @@
final Task task = stack.getAllTasks().get(0);
final ArrayList<ActivityRecord> stackActivities = new ArrayList<>();
- for (int i = 0; i < task.getChildCount(); i++) {
- stackActivities.add(task.getChildAt(i));
- }
+ task.forAllActivities((Consumer<ActivityRecord>) stackActivities::add, false);
assertEquals("Expecting " + Arrays.deepToString(activities) + " got " + stackActivities,
stackActivities.size(), activities != null ? activities.length : 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index c8f81f4..d756f9a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -471,8 +471,9 @@
// Add an extra activity on top of the root one
new ActivityBuilder(mService).setTask(task).build();
- assertEquals("The root activity in the task must be reported.",
- 0, task.findRootIndex(false /* effectiveRoot*/));
+ assertEquals("The root activity in the task must be reported.", task.getChildAt(0),
+ task.getRootActivity(
+ true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/**
@@ -483,14 +484,15 @@
public void testFindRootIndex_finishing() {
final Task task = getTestTask();
// Add extra two activities and mark the two on the bottom as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
activity1.finishing = true;
new ActivityBuilder(mService).setTask(task).build();
assertEquals("The first non-finishing activity in the task must be reported.",
- 2, task.findRootIndex(false /* effectiveRoot*/));
+ task.getChildAt(2), task.getRootActivity(
+ true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/**
@@ -504,7 +506,8 @@
new ActivityBuilder(mService).setTask(task).build();
assertEquals("The root activity in the task must be reported.",
- 0, task.findRootIndex(true /* effectiveRoot*/));
+ task.getChildAt(0), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/**
@@ -516,14 +519,15 @@
final Task task = getTestTask();
// Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and
// one above as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
activity1.finishing = true;
new ActivityBuilder(mService).setTask(task).build();
assertEquals("The first non-finishing activity and non-relinquishing task identity "
- + "must be reported.", 2, task.findRootIndex(true /* effectiveRoot*/));
+ + "must be reported.", task.getChildAt(2), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/**
@@ -534,10 +538,11 @@
public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() {
final Task task = getTestTask();
// Set relinquishTaskIdentity for the only activity in the task
- task.getChildAt(0).info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
+ task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
assertEquals("The root activity in the task must be reported.",
- 0, task.findRootIndex(true /* effectiveRoot*/));
+ task.getChildAt(0), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/**
@@ -548,13 +553,14 @@
public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() {
final Task task = getTestTask();
// Set relinquishTaskIdentity for all activities in the task
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
assertEquals("The topmost activity in the task must be reported.",
- task.getChildCount() - 1, task.findRootIndex(true /* effectiveRoot*/));
+ task.getChildAt(task.getChildCount() - 1), task.getRootActivity(
+ false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/));
}
/** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */
@@ -565,7 +571,7 @@
new ActivityBuilder(mService).setTask(task).build();
assertEquals("The root activity in the task must be reported.",
- task.getChildAt(0), task.getRootActivity());
+ task.getBottomMostActivity(), task.getRootActivity());
}
/**
@@ -577,7 +583,7 @@
// Add an extra activity on top of the root one
new ActivityBuilder(mService).setTask(task).build();
// Mark the root as finishing
- task.getChildAt(0).finishing = true;
+ task.getBottomMostActivity().finishing = true;
assertEquals("The first non-finishing activity in the task must be reported.",
task.getChildAt(1), task.getRootActivity());
@@ -590,13 +596,13 @@
public void testGetRootActivity_relinquishTaskIdentity() {
final Task task = getTestTask();
// Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top of the root one.
new ActivityBuilder(mService).setTask(task).build();
assertEquals("The root activity in the task must be reported.",
- task.getChildAt(0), task.getRootActivity());
+ task.getBottomMostActivity(), task.getRootActivity());
}
/**
@@ -607,7 +613,7 @@
public void testGetRootActivity_allFinishing() {
final Task task = getTestTask();
// Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
// Add an extra activity on top of the root one and mark it as finishing
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -623,7 +629,7 @@
public void testIsRootActivity() {
final Task task = getTestTask();
// Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
// Add an extra activity on top of the root one.
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -640,7 +646,7 @@
public void testIsRootActivity_allFinishing() {
final Task task = getTestTask();
// Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
// Add an extra activity on top of the root one and mark it as finishing
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -657,10 +663,10 @@
@Test
public void testGetTaskForActivity() {
final Task task0 = getTestTask();
- final ActivityRecord activity0 = task0.getChildAt(0);
+ final ActivityRecord activity0 = task0.getBottomMostActivity();
final Task task1 = getTestTask();
- final ActivityRecord activity1 = task1.getChildAt(0);
+ final ActivityRecord activity1 = task1.getBottomMostActivity();
assertEquals(task0.mTaskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
@@ -676,7 +682,7 @@
public void testGetTaskForActivity_onlyRoot_finishing() {
final Task task = getTestTask();
// Make the current root activity finishing
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
// Add an extra activity on top - this will be the new root
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -699,7 +705,7 @@
public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() {
final Task task = getTestTask();
// Make the current root activity relinquish task identity
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top - this will be the new root
final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build();
@@ -722,7 +728,7 @@
public void testGetTaskForActivity_notOnlyRoot() {
final Task task = getTestTask();
// Mark the bottom-most activity as finishing.
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.finishing = true;
// Add an extra activity on top of the root one and make it relinquish task identity
@@ -747,7 +753,7 @@
public void testUpdateEffectiveIntent() {
// Test simple case with a single activity.
final Task task = getTestTask();
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
spyOn(task);
task.updateEffectiveIntent();
@@ -762,7 +768,7 @@
public void testUpdateEffectiveIntent_rootFinishing() {
// Test simple case with a single activity.
final Task task = getTestTask();
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
// Mark the bottom-most activity as finishing.
activity0.finishing = true;
// Add an extra activity on top of the root one
@@ -782,7 +788,7 @@
public void testUpdateEffectiveIntent_allFinishing() {
// Test simple case with a single activity.
final Task task = getTestTask();
- final ActivityRecord activity0 = task.getChildAt(0);
+ final ActivityRecord activity0 = task.getBottomMostActivity();
// Mark the bottom-most activity as finishing.
activity0.finishing = true;
// Add an extra activity on top of the root one and make it relinquish task identity
@@ -818,7 +824,7 @@
task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds());
}
- private byte[] serializeToBytes(Task r) throws IOException, XmlPullParserException {
+ private byte[] serializeToBytes(Task r) throws Exception {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
final XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(os, "UTF-8");
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index b428530..221f8f1 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,6 +16,10 @@
package android.telecom;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -32,6 +36,7 @@
* Builder class for {@link ConnectionRequest}
* @hide
*/
+ @TestApi // For convenience in CTS tests
public static final class Builder {
private PhoneAccountHandle mAccountHandle;
private Uri mAddress;
@@ -48,7 +53,7 @@
* Sets the phone account handle for the resulting {@link ConnectionRequest}
* @param accountHandle The accountHandle which should be used to place the call.
*/
- public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
+ public @NonNull Builder setAccountHandle(@NonNull PhoneAccountHandle accountHandle) {
this.mAccountHandle = accountHandle;
return this;
}
@@ -58,7 +63,7 @@
* @param address The address(e.g., phone number) to which the {@link Connection} is to
* connect.
*/
- public Builder setAddress(Uri address) {
+ public @NonNull Builder setAddress(@NonNull Uri address) {
this.mAddress = address;
return this;
}
@@ -67,7 +72,7 @@
* Sets the extras bundle for the resulting {@link ConnectionRequest}
* @param extras Application-specific extra data.
*/
- public Builder setExtras(Bundle extras) {
+ public @NonNull Builder setExtras(@NonNull Bundle extras) {
this.mExtras = extras;
return this;
}
@@ -76,7 +81,7 @@
* Sets the video state for the resulting {@link ConnectionRequest}
* @param videoState Determines the video state for the connection.
*/
- public Builder setVideoState(int videoState) {
+ public @NonNull Builder setVideoState(int videoState) {
this.mVideoState = videoState;
return this;
}
@@ -85,7 +90,7 @@
* Sets the Telecom call ID for the resulting {@link ConnectionRequest}
* @param telecomCallId The telecom call ID.
*/
- public Builder setTelecomCallId(String telecomCallId) {
+ public @NonNull Builder setTelecomCallId(@NonNull String telecomCallId) {
this.mTelecomCallId = telecomCallId;
return this;
}
@@ -97,7 +102,7 @@
* its own incoming call UI for an incoming call. When
* {@code false}, Telecom shows the incoming call UI.
*/
- public Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) {
+ public @NonNull Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) {
this.mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
return this;
}
@@ -107,7 +112,8 @@
* resulting {@link ConnectionRequest}
* @param rttPipeFromInCall The data pipe to read from.
*/
- public Builder setRttPipeFromInCall(ParcelFileDescriptor rttPipeFromInCall) {
+ public @NonNull Builder setRttPipeFromInCall(
+ @NonNull ParcelFileDescriptor rttPipeFromInCall) {
this.mRttPipeFromInCall = rttPipeFromInCall;
return this;
}
@@ -117,12 +123,16 @@
* resulting {@link ConnectionRequest}
* @param rttPipeToInCall The data pipe to write to.
*/
- public Builder setRttPipeToInCall(ParcelFileDescriptor rttPipeToInCall) {
+ public @NonNull Builder setRttPipeToInCall(@NonNull ParcelFileDescriptor rttPipeToInCall) {
this.mRttPipeToInCall = rttPipeToInCall;
return this;
}
- public ConnectionRequest build() {
+ /**
+ * Build the {@link ConnectionRequest}
+ * @return Result of the builder
+ */
+ public @NonNull ConnectionRequest build() {
return new ConnectionRequest(
mAccountHandle,
mAddress,
@@ -261,7 +271,9 @@
* @return The Telecom ID.
* @hide
*/
- public String getTelecomCallId() {
+ @SystemApi
+ @TestApi
+ public @Nullable String getTelecomCallId() {
return mTelecomCallId;
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4016943..39d741c 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1029,12 +1029,16 @@
* by the user.
*
* @param includeDisabledAccounts When {@code true}, disabled phone accounts will be included,
- * when {@code false}, only
+ * when {@code false}, only enabled phone accounts will be
+ * included.
* @return A list of {@code PhoneAccountHandle} objects.
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
- public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
+ @SystemApi
+ @TestApi
+ @RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
+ public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccounts(
+ boolean includeDisabledAccounts) {
try {
if (isServiceConnected()) {
return getTelecomService().getCallCapablePhoneAccounts(
diff --git a/core/java/android/service/carrier/CarrierIdentifier.aidl b/telephony/java/android/service/carrier/CarrierIdentifier.aidl
similarity index 100%
rename from core/java/android/service/carrier/CarrierIdentifier.aidl
rename to telephony/java/android/service/carrier/CarrierIdentifier.aidl
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/telephony/java/android/service/carrier/CarrierIdentifier.java
similarity index 100%
rename from core/java/android/service/carrier/CarrierIdentifier.java
rename to telephony/java/android/service/carrier/CarrierIdentifier.java
diff --git a/core/java/android/service/carrier/CarrierService.java b/telephony/java/android/service/carrier/CarrierService.java
similarity index 100%
rename from core/java/android/service/carrier/CarrierService.java
rename to telephony/java/android/service/carrier/CarrierService.java
diff --git a/core/java/android/service/carrier/ICarrierService.aidl b/telephony/java/android/service/carrier/ICarrierService.aidl
similarity index 100%
rename from core/java/android/service/carrier/ICarrierService.aidl
rename to telephony/java/android/service/carrier/ICarrierService.aidl
diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.aidl b/telephony/java/android/service/euicc/DownloadSubscriptionResult.aidl
similarity index 100%
rename from core/java/android/service/euicc/DownloadSubscriptionResult.aidl
rename to telephony/java/android/service/euicc/DownloadSubscriptionResult.aidl
diff --git a/core/java/android/service/euicc/DownloadSubscriptionResult.java b/telephony/java/android/service/euicc/DownloadSubscriptionResult.java
similarity index 100%
rename from core/java/android/service/euicc/DownloadSubscriptionResult.java
rename to telephony/java/android/service/euicc/DownloadSubscriptionResult.java
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.aidl b/telephony/java/android/service/euicc/EuiccProfileInfo.aidl
similarity index 100%
rename from core/java/android/service/euicc/EuiccProfileInfo.aidl
rename to telephony/java/android/service/euicc/EuiccProfileInfo.aidl
diff --git a/core/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
similarity index 100%
rename from core/java/android/service/euicc/EuiccProfileInfo.java
rename to telephony/java/android/service/euicc/EuiccProfileInfo.java
diff --git a/core/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
similarity index 100%
rename from core/java/android/service/euicc/EuiccService.java
rename to telephony/java/android/service/euicc/EuiccService.java
diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl
similarity index 100%
rename from core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl
rename to telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.aidl
diff --git a/core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java b/telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
similarity index 100%
rename from core/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
rename to telephony/java/android/service/euicc/GetDefaultDownloadableSubscriptionListResult.java
diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl
similarity index 100%
rename from core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl
rename to telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.aidl
diff --git a/core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java b/telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
similarity index 100%
rename from core/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
rename to telephony/java/android/service/euicc/GetDownloadableSubscriptionMetadataResult.java
diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl b/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl
similarity index 100%
rename from core/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl
rename to telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.aidl
diff --git a/core/java/android/service/euicc/GetEuiccProfileInfoListResult.java b/telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java
similarity index 100%
rename from core/java/android/service/euicc/GetEuiccProfileInfoListResult.java
rename to telephony/java/android/service/euicc/GetEuiccProfileInfoListResult.java
diff --git a/core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl b/telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl
rename to telephony/java/android/service/euicc/IDeleteSubscriptionCallback.aidl
diff --git a/core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl b/telephony/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
rename to telephony/java/android/service/euicc/IDownloadSubscriptionCallback.aidl
diff --git a/core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl b/telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl
rename to telephony/java/android/service/euicc/IEraseSubscriptionsCallback.aidl
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/telephony/java/android/service/euicc/IEuiccService.aidl
similarity index 100%
rename from core/java/android/service/euicc/IEuiccService.aidl
rename to telephony/java/android/service/euicc/IEuiccService.aidl
diff --git a/core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl b/telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl
rename to telephony/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl
diff --git a/core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl b/telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl
rename to telephony/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl
diff --git a/core/java/android/service/euicc/IGetEidCallback.aidl b/telephony/java/android/service/euicc/IGetEidCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetEidCallback.aidl
rename to telephony/java/android/service/euicc/IGetEidCallback.aidl
diff --git a/core/java/android/service/euicc/IGetEuiccInfoCallback.aidl b/telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetEuiccInfoCallback.aidl
rename to telephony/java/android/service/euicc/IGetEuiccInfoCallback.aidl
diff --git a/core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl b/telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl
rename to telephony/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl
diff --git a/core/java/android/service/euicc/IGetOtaStatusCallback.aidl b/telephony/java/android/service/euicc/IGetOtaStatusCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IGetOtaStatusCallback.aidl
rename to telephony/java/android/service/euicc/IGetOtaStatusCallback.aidl
diff --git a/core/java/android/service/euicc/IOtaStatusChangedCallback.aidl b/telephony/java/android/service/euicc/IOtaStatusChangedCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IOtaStatusChangedCallback.aidl
rename to telephony/java/android/service/euicc/IOtaStatusChangedCallback.aidl
diff --git a/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl b/telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
rename to telephony/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
diff --git a/core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl b/telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl
rename to telephony/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl
diff --git a/core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl b/telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl
rename to telephony/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index a0aa60b..0320f75 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -80,6 +80,7 @@
public static final int EUTRAN = 3;
public static final int CDMA2000 = 4;
public static final int IWLAN = 5;
+ public static final int NGRAN = 6;
/** @hide */
private AccessNetworkType() {}
@@ -93,6 +94,7 @@
case EUTRAN: return "EUTRAN";
case CDMA2000: return "CDMA2000";
case IWLAN: return "IWLAN";
+ case NGRAN: return "NGRAN";
default: return Integer.toString(type);
}
}
@@ -247,6 +249,54 @@
private CdmaBands() {};
}
+ /**
+ * Frequency bands for NGRAN
+ */
+ public static final class NgranBands {
+ /** FR1 bands */
+ public static final int BAND_1 = 1;
+ public static final int BAND_2 = 2;
+ public static final int BAND_3 = 3;
+ public static final int BAND_5 = 5;
+ public static final int BAND_7 = 7;
+ public static final int BAND_8 = 8;
+ public static final int BAND_12 = 12;
+ public static final int BAND_20 = 20;
+ public static final int BAND_25 = 25;
+ public static final int BAND_28 = 28;
+ public static final int BAND_34 = 34;
+ public static final int BAND_38 = 38;
+ public static final int BAND_39 = 39;
+ public static final int BAND_40 = 40;
+ public static final int BAND_41 = 41;
+ public static final int BAND_50 = 50;
+ public static final int BAND_51 = 51;
+ public static final int BAND_66 = 66;
+ public static final int BAND_70 = 70;
+ public static final int BAND_71 = 71;
+ public static final int BAND_74 = 74;
+ public static final int BAND_75 = 75;
+ public static final int BAND_76 = 76;
+ public static final int BAND_77 = 77;
+ public static final int BAND_78 = 78;
+ public static final int BAND_79 = 79;
+ public static final int BAND_80 = 80;
+ public static final int BAND_81 = 81;
+ public static final int BAND_82 = 82;
+ public static final int BAND_83 = 83;
+ public static final int BAND_84 = 84;
+ public static final int BAND_86 = 86;
+
+ /** FR2 bands */
+ public static final int BAND_257 = 257;
+ public static final int BAND_258 = 258;
+ public static final int BAND_260 = 260;
+ public static final int BAND_261 = 261;
+
+ /** @hide */
+ private NgranBands() {};
+ }
+
/** @hide */
private AccessNetworkConstants() {};
}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 79b3756..fe273b2 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -193,6 +193,17 @@
}
}
+ private static String getAppOpsString(String manifestPermission) {
+ switch (manifestPermission) {
+ case Manifest.permission.ACCESS_FINE_LOCATION:
+ return AppOpsManager.OPSTR_FINE_LOCATION;
+ case Manifest.permission.ACCESS_COARSE_LOCATION:
+ return AppOpsManager.OPSTR_COARSE_LOCATION;
+ default:
+ return null;
+ }
+ }
+
private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
LocationPermissionQuery query, String permissionToCheck) {
String locationTypeForLog =
@@ -206,8 +217,8 @@
if (hasManifestPermission) {
// Only check the app op if the app has the permission.
int appOpMode = context.getSystemService(AppOpsManager.class)
- .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
- query.callingUid, query.callingPackage, query.callingFeatureId, null);
+ .noteOpNoThrow(getAppOpsString(permissionToCheck), query.callingUid,
+ query.callingPackage, query.callingFeatureId, null);
if (appOpMode == AppOpsManager.MODE_ALLOWED) {
// If the app did everything right, return without logging.
return LocationPermissionResult.ALLOWED;
diff --git a/telephony/java/android/telephony/RadioAccessSpecifier.java b/telephony/java/android/telephony/RadioAccessSpecifier.java
index 690e44a..a403095 100644
--- a/telephony/java/android/telephony/RadioAccessSpecifier.java
+++ b/telephony/java/android/telephony/RadioAccessSpecifier.java
@@ -97,8 +97,9 @@
* Returns the frequency bands that need to be scanned.
*
* The returned value is defined in either of {@link AccessNetworkConstants.GeranBand},
- * {@link AccessNetworkConstants.UtranBand} and {@link AccessNetworkConstants.EutranBand}, and
- * it depends on the returned value of {@link #getRadioAccessNetwork()}.
+ * {@link AccessNetworkConstants.UtranBand}, {@link AccessNetworkConstants.EutranBand},
+ * and {@link AccessNetworkConstants.NgranBands}, and it depends on
+ * the returned value of {@link #getRadioAccessNetwork()}.
*/
public int[] getBands() {
return mBands == null ? null : mBands.clone();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 1dd1185..fc26122 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -86,7 +86,6 @@
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telecom.ITelecomService;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.INumberVerificationCallback;
import com.android.internal.telephony.IOns;
@@ -5176,13 +5175,11 @@
* @return the current call state.
*/
public @CallState int getCallState() {
- try {
- ITelecomService telecom = getTelecomService();
- if (telecom != null) {
- return telecom.getCallState();
+ if (mContext != null) {
+ TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ if (telecomManager != null) {
+ return telecomManager.getCallState();
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelecomService#getCallState", e);
}
return CALL_STATE_IDLE;
}
@@ -5341,13 +5338,6 @@
return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
}
- /**
- * @hide
- */
- private ITelecomService getTelecomService() {
- return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
- }
-
private ITelephonyRegistry getTelephonyRegistry() {
return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
}
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 2cdf2f6..dcea9bb 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -23,7 +23,7 @@
import android.util.SparseIntArray;
import com.android.internal.telephony.cdma.sms.UserData;
-import com.android.internal.util.XmlUtils;
+import com.android.internal.telephony.util.XmlUtils;
import dalvik.annotation.compat.UnsupportedAppUsage;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 6e20621..96ddf22 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -390,7 +390,6 @@
private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
int uid, String callingPackage, String message) {
boolean isPreinstalled = false;
- boolean isPrivApp = false;
ApplicationInfo callingPackageInfo = null;
try {
callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
@@ -398,9 +397,6 @@
if (callingPackageInfo != null) {
if (callingPackageInfo.isSystemApp()) {
isPreinstalled = true;
- if (callingPackageInfo.isPrivilegedApp()) {
- isPrivApp = true;
- }
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -423,10 +419,10 @@
}
invokedMethods.add(message);
StatsLog.write(StatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED, callingPackage, message,
- isPreinstalled, isPrivApp);
+ isPreinstalled, false);
}
Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message
- + ":isPreinstalled=" + isPreinstalled + ":isPrivApp=" + isPrivApp);
+ + ":isPreinstalled=" + isPreinstalled);
// if the target SDK is pre-Q then check if the calling package would have previously
// had access to device identifiers.
if (callingPackageInfo != null && (
diff --git a/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
new file mode 100644
index 0000000..a28d65c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/TelephonyUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.telephony.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+
+import java.io.PrintWriter;
+
+/**
+ * This class provides various util functions
+ */
+public final class TelephonyUtils {
+ public static boolean IS_USER = "user".equals(android.os.Build.TYPE);
+ public static boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1;
+
+ /**
+ * Verify that caller holds {@link android.Manifest.permission#DUMP}.
+ *
+ * @return true if access should be granted.
+ */
+ public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump " + tag + " from from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " due to missing android.permission.DUMP permission");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /** Returns an empty string if the input is {@code null}. */
+ public static String emptyIfNull(@Nullable String str) {
+ return str == null ? "" : str;
+ }
+
+ /** Throws a {@link RuntimeException} that wrapps the {@link RemoteException}. */
+ public static RuntimeException rethrowAsRuntimeException(RemoteException remoteException) {
+ throw new RuntimeException(remoteException);
+ }
+
+ /**
+ * Returns a {@link ComponentInfo} from the {@link ResolveInfo},
+ * or throws an {@link IllegalStateException} if not available.
+ */
+ public static ComponentInfo getComponentInfo(@NonNull ResolveInfo resolveInfo) {
+ if (resolveInfo.activityInfo != null) return resolveInfo.activityInfo;
+ if (resolveInfo.serviceInfo != null) return resolveInfo.serviceInfo;
+ if (resolveInfo.providerInfo != null) return resolveInfo.providerInfo;
+ throw new IllegalStateException("Missing ComponentInfo!");
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/util/XmlUtils.java b/telephony/java/com/android/internal/telephony/util/XmlUtils.java
new file mode 100644
index 0000000..72c5d3a
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/util/XmlUtils.java
@@ -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 com.android.internal.telephony.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** Utility methods for XML operations. */
+public final class XmlUtils {
+ private XmlUtils() {}
+
+ /**
+ * Moves parser to the first start tag, and expects the tag name being {@code firstElementName}.
+ */
+ public static void beginDocument(XmlPullParser parser, String firstElementName)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) {
+ // no-op
+ }
+
+ if (type != parser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ if (!parser.getName().equals(firstElementName)) {
+ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName()
+ + ", expected " + firstElementName);
+ }
+ }
+
+ /**
+ * Moves parser to the next start tag.
+ */
+ public static void nextElement(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) {
+ // no-op
+ }
+ }
+
+ /**
+ * Moves parser to the next start tag within the {@code outerDepth}.
+ */
+ public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
+ throws IOException, XmlPullParserException {
+ for (;;) {
+ int type = parser.next();
+ if (type == XmlPullParser.END_DOCUMENT
+ || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
+ return false;
+ }
+ if (type == XmlPullParser.START_TAG && parser.getDepth() == outerDepth + 1) {
+ return true;
+ }
+ }
+ }
+}
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 45b236c..0208c3a 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -470,6 +470,13 @@
}
@Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp,
+ Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void sendStickyBroadcast(Intent intent) {
throw new UnsupportedOperationException();
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 0787d82..51bae3a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -29,9 +29,13 @@
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.RuntimeShader;
import android.os.Bundle;
import android.view.View;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
@SuppressWarnings({"UnusedDeclaration"})
public class ColorFiltersMutateActivity extends Activity {
@Override
@@ -47,12 +51,21 @@
private final Paint mColorMatrixPaint;
private final Paint mLightingPaint;
private final Paint mBlendPaint;
+ private final Paint mShaderPaint;
private float mSaturation = 0.0f;
private int mLightAdd = 0;
private int mLightMul = 0;
private int mPorterDuffColor = 0;
+ static final String sSkSL =
+ "uniform float param1;\n"
+ + "void main(float x, float y, inout half4 color) {\n"
+ + "color = half4(color.r, half(param1), color.b, 1.0);\n"
+ + "}\n";
+
+ private byte[] mUniforms = new byte[4];
+
BitmapsView(Context c) {
super(c);
@@ -70,6 +83,10 @@
mBlendPaint = new Paint();
mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
+ mShaderPaint = new Paint();
+ mShaderPaint.setShader(new RuntimeShader(sSkSL, mUniforms, true));
+ setShaderParam1(0.0f);
+
ObjectAnimator sat = ObjectAnimator.ofFloat(this, "saturation", 1.0f);
sat.setDuration(1000);
sat.setRepeatCount(ObjectAnimator.INFINITE);
@@ -96,6 +113,12 @@
color.setRepeatCount(ObjectAnimator.INFINITE);
color.setRepeatMode(ObjectAnimator.REVERSE);
color.start();
+
+ ObjectAnimator shaderUniform = ObjectAnimator.ofFloat(this, "shaderParam1", 1.0f);
+ shaderUniform.setDuration(1000);
+ shaderUniform.setRepeatCount(ObjectAnimator.INFINITE);
+ shaderUniform.setRepeatMode(ObjectAnimator.REVERSE);
+ shaderUniform.start();
}
public int getPorterDuffColor() {
@@ -148,6 +171,23 @@
return mSaturation;
}
+ public void setShaderParam1(float value) {
+ RuntimeShader shader = (RuntimeShader) mShaderPaint.getShader();
+ ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.putFloat(value);
+ shader.updateUniforms(mUniforms);
+ invalidate();
+ }
+
+ // If either valueFrom or valueTo is null, then a getter function will also be derived
+ // and called by the animator class.
+ public float getShaderParam1() {
+ ByteBuffer buffer = ByteBuffer.wrap(mUniforms);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ return buffer.getFloat();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -163,6 +203,10 @@
canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
canvas.drawBitmap(mBitmap1, 0.0f, 0.0f, mBlendPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap1.getHeight());
+ canvas.drawRect(0.0f, 0.0f, mBitmap1.getWidth(), mBitmap1.getHeight(),
+ mShaderPaint);
canvas.restore();
canvas.save();
@@ -174,6 +218,10 @@
canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
canvas.drawBitmap(mBitmap2, 0.0f, 0.0f, mBlendPaint);
+
+ canvas.translate(0.0f, 50.0f + mBitmap2.getHeight());
+ canvas.drawRoundRect(0.0f, 0.0f, mBitmap2.getWidth(), mBitmap2.getHeight(), 20, 20,
+ mShaderPaint);
canvas.restore();
}
}
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 06b58fd..e308781 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -670,11 +670,11 @@
public void testPackageHealthCheckStateTransitions() {
TestController controller = new TestController();
PackageWatchdog wd = createWatchdog(controller, true /* withPackagesReady */);
- MonitoredPackage m1 = wd.new MonitoredPackage(APP_A, LONG_DURATION,
+ MonitoredPackage m1 = wd.newMonitoredPackage(APP_A, LONG_DURATION,
false /* hasPassedHealthCheck */);
- MonitoredPackage m2 = wd.new MonitoredPackage(APP_B, LONG_DURATION, false);
- MonitoredPackage m3 = wd.new MonitoredPackage(APP_C, LONG_DURATION, false);
- MonitoredPackage m4 = wd.new MonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true);
+ MonitoredPackage m2 = wd.newMonitoredPackage(APP_B, LONG_DURATION, false);
+ MonitoredPackage m3 = wd.newMonitoredPackage(APP_C, LONG_DURATION, false);
+ MonitoredPackage m4 = wd.newMonitoredPackage(APP_D, LONG_DURATION, SHORT_DURATION, true);
// Verify transition: inactive -> active -> passed
// Verify initially inactive
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index 10f27e2..b2f384a 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -45,7 +45,6 @@
name: "FrameworksNetTests",
defaults: ["FrameworksNetTests-jni-defaults"],
srcs: [
- ":tethering-tests-src",
"java/**/*.java",
"java/**/*.kt",
],
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 334b26d..25028fb 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -32,6 +32,7 @@
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkRequest
import android.net.TestNetworkStackClient
+import android.net.TetheringManager
import android.net.metrics.IpConnectivityLog
import android.os.ConditionVariable
import android.os.IBinder
@@ -48,7 +49,6 @@
import com.android.server.connectivity.IpConnectivityMetrics
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.ProxyTracker
-import com.android.server.connectivity.Tethering
import com.android.server.net.NetworkPolicyManagerInternal
import com.android.testutils.TestableNetworkCallback
import org.junit.After
@@ -169,8 +169,7 @@
val deps = spy(ConnectivityService.Dependencies())
doReturn(networkStackClient).`when`(deps).networkStack
doReturn(metricsLogger).`when`(deps).metricsLogger
- doReturn(mock(Tethering::class.java)).`when`(deps).makeTethering(
- any(), any(), any(), any(), any())
+ doReturn(mock(TetheringManager::class.java)).`when`(deps).getTetheringManager()
doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 7ea9bcf..fd3ed7d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -96,6 +96,7 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
@@ -163,6 +164,7 @@
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.SocketKeepalive;
+import android.net.TetheringManager;
import android.net.UidRange;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -197,6 +199,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
@@ -210,7 +213,6 @@
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
-import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -305,6 +307,7 @@
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ @Mock IBatteryStats mBatteryStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@@ -1130,11 +1133,12 @@
doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(systemProperties).when(deps).getSystemProperties();
- doReturn(mock(Tethering.class)).when(deps).makeTethering(any(), any(), any(), any(), any());
+ doReturn(mock(TetheringManager.class)).when(deps).getTetheringManager();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(mMetricsService).when(deps).getMetricsLogger();
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
+ doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
doReturn(true).when(deps).hasService(Context.ETHERNET_SERVICE);
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -5640,6 +5644,37 @@
mCm.unregisterNetworkCallback(defaultCallback);
}
+ @Ignore // 40%+ flakiness : figure out why and re-enable.
+ @Test
+ public final void testBatteryStatsNetworkType() throws Exception {
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("cell0");
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
+ reset(mBatteryStatsService);
+
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wifi0");
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ mWiFiNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
+ TYPE_WIFI);
+ reset(mBatteryStatsService);
+
+ mCellNetworkAgent.disconnect();
+
+ cellLp.setInterfaceName("wifi0");
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.connect(true);
+ waitForIdle();
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
+ }
+
/**
* Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
*/
@@ -5680,25 +5715,28 @@
mCm.registerNetworkCallback(networkRequest, networkCallback);
// Prepare ipv6 only link properties.
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- final int cellNetId = mCellNetworkAgent.getNetwork().netId;
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
cellLp.addLinkAddress(myIpv6);
cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mNetworkManagementService);
reset(mMockDnsResolver);
reset(mMockNetd);
+ reset(mBatteryStatsService);
when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfig(myIpv4));
// Connect with ipv6 link properties. Expect prefix discovery to be started.
- mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(true);
+ final int cellNetId = mCellNetworkAgent.getNetwork().netId;
+ waitForIdle();
verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
+ verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
+ TYPE_MOBILE);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -5714,6 +5752,11 @@
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(any());
+ // Make sure BatteryStats was not told about any v4- interfaces, as none should have
+ // come online yet.
+ waitForIdle();
+ verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
@@ -5760,6 +5803,11 @@
assertEquals(1, resolvrParams.servers.length);
assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
+ for (final LinkProperties stackedLp : stackedLpsAfterChange) {
+ verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
+ TYPE_MOBILE);
+ }
+
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 56bff8f..aea2432 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -264,8 +264,8 @@
&options_.keep_raw_values);
AddOptionalFlag("--no-compress-regex",
"Do not compress extensions matching the regular expression. Remember to\n"
- " use the '$' symbol for end of line. Uses a non case-sensitive\n"
- " ECMAScript regular expression grammar.",
+ "use the '$' symbol for end of line. Uses a case-sensitive ECMAScript"
+ "regular expression grammar.",
&no_compress_regex);
AddOptionalSwitch("--warn-manifest-validation",
"Treat manifest validation errors as warnings.",
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index e2c65ba7..7214f1a 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -436,9 +436,9 @@
}
std::regex GetRegularExpression(const std::string &input) {
- // Standard ECMAScript grammar plus case insensitive.
+ // Standard ECMAScript grammar.
std::regex case_insensitive(
- input, std::regex_constants::icase | std::regex_constants::ECMAScript);
+ input, std::regex_constants::ECMAScript);
return case_insensitive;
}
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 2f090bb..ac1f981 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -383,13 +383,32 @@
EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
}
-// TODO(127793905): Enable test
-/*TEST(UtilTest, RegularExperssions) {
+TEST (UtilTest, RegularExperssionsSimple) {
std::string valid(".bc$");
std::regex expression = GetRegularExpression(valid);
EXPECT_TRUE(std::regex_search("file.abc", expression));
EXPECT_TRUE(std::regex_search("file.123bc", expression));
EXPECT_FALSE(std::regex_search("abc.zip", expression));
-}*/
+}
+
+TEST (UtilTest, RegularExpressionComplex) {
+ std::string valid("\\.(d|D)(e|E)(x|X)$");
+ std::regex expression = GetRegularExpression(valid);
+ EXPECT_TRUE(std::regex_search("file.dex", expression));
+ EXPECT_TRUE(std::regex_search("file.DEX", expression));
+ EXPECT_TRUE(std::regex_search("file.dEx", expression));
+ EXPECT_FALSE(std::regex_search("file.dexx", expression));
+ EXPECT_FALSE(std::regex_search("dex.file", expression));
+ EXPECT_FALSE(std::regex_search("file.adex", expression));
+}
+
+TEST (UtilTest, RegularExpressionNonEnglish) {
+ std::string valid("\\.(k|K)(o|O)(ń|Ń)(c|C)(ó|Ó)(w|W)(k|K)(a|A)$");
+ std::regex expression = GetRegularExpression(valid);
+ EXPECT_TRUE(std::regex_search("file.końcówka", expression));
+ EXPECT_TRUE(std::regex_search("file.KOŃCÓWKA", expression));
+ EXPECT_TRUE(std::regex_search("file.kOńcÓwkA", expression));
+ EXPECT_FALSE(std::regex_search("file.koncowka", expression));
+}
} // namespace aapt
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 4a89c66..032f66a 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -142,6 +142,8 @@
boolean startSoftAp(in WifiConfiguration wifiConfig);
+ boolean startTetheredHotspot(in SoftApConfiguration softApConfig);
+
boolean stopSoftAp();
int startLocalOnlyHotspot(in ILocalOnlyHotspotCallback callback, String packageName,
@@ -159,8 +161,14 @@
@UnsupportedAppUsage
WifiConfiguration getWifiApConfiguration();
+ SoftApConfiguration getSoftApConfiguration();
+
boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
+ boolean setSoftApConfiguration(in SoftApConfiguration softApConfig, String packageName);
+
+ void notifyUserOfApBandConversion(String packageName);
+
void enableTdls(String remoteIPAddress, boolean enable);
void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
@@ -184,6 +192,10 @@
void restoreBackupData(in byte[] data);
+ byte[] retrieveSoftApBackupData();
+
+ void restoreSoftApBackupData(in byte[] data);
+
void restoreSupplicantBackupData(in byte[] supplicantData, in byte[] ipConfigData);
void startSubscriptionProvisioning(in OsuProvider provider, in IProvisioningCallback callback);
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 4cc8653..8030bd6 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -16,28 +16,34 @@
package android.net.wifi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.MacAddress;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
- * WiFi configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
+ * Configuration for a soft access point (a.k.a. Soft AP, SAP, Hotspot).
*
* This is input for the framework provided by a client app, i.e. it exposes knobs to instruct the
- * framework how it should open a hotspot. It is not meant to describe the network as it will be
- * seen by clients; this role is currently served by {@link WifiConfiguration} (see
- * {@link WifiManager.LocalOnlyHotspotReservation#getWifiConfiguration()}).
+ * framework how it should configure a hotspot.
*
- * System apps can use this to configure a local-only hotspot using
+ * System apps can use this to configure a tethered hotspot using
+ * {@link WifiManager#startTetheredHotspot(SoftApConfiguration)} and
+ * {@link WifiManager#setSoftApConfiguration(SoftApConfiguration)}
+ * or local-only hotspot using
* {@link WifiManager#startLocalOnlyHotspot(SoftApConfiguration, Executor,
* WifiManager.LocalOnlyHotspotCallback)}.
*
@@ -48,25 +54,106 @@
*/
@SystemApi
public final class SoftApConfiguration implements Parcelable {
+
+ /**
+ * 2GHz band.
+ * @hide
+ */
+ @SystemApi
+ public static final int BAND_2GHZ = 0;
+
+ /**
+ * 5GHz band.
+ * @hide
+ */
+ @SystemApi
+ public static final int BAND_5GHZ = 1;
+
+ /**
+ * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+ * operating country code and current radio conditions.
+ * @hide
+ */
+ @SystemApi
+ public static final int BAND_ANY = -1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BAND_TYPE_" }, value = {
+ BAND_2GHZ,
+ BAND_5GHZ,
+ BAND_ANY,
+ })
+ public @interface BandType {}
+
/**
* SSID for the AP, or null for a framework-determined SSID.
*/
private final @Nullable String mSsid;
+
/**
* BSSID for the AP, or null to use a framework-determined BSSID.
*/
private final @Nullable MacAddress mBssid;
+
/**
* Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
*/
private final @Nullable String mWpa2Passphrase;
+ /**
+ * This is a network that does not broadcast its SSID, so an
+ * SSID-specific probe request must be used for scans.
+ */
+ private final boolean mHiddenSsid;
+
+ /**
+ * The operating band of the AP.
+ * One of the band types from {@link @BandType}.
+ */
+ private final @BandType int mBand;
+
+ /**
+ * The operating channel of the AP.
+ */
+ private final int mChannel;
+
+ /**
+ * The operating security type of the AP.
+ * One of the security types from {@link @SecurityType}
+ */
+ private final @SecurityType int mSecurityType;
+
+ /**
+ * Security types we support.
+ */
+ /** @hide */
+ @SystemApi
+ public static final int SECURITY_TYPE_OPEN = 0;
+
+ /** @hide */
+ @SystemApi
+ public static final int SECURITY_TYPE_WPA2_PSK = 1;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SECURITY_TYPE" }, value = {
+ SECURITY_TYPE_OPEN,
+ SECURITY_TYPE_WPA2_PSK,
+ })
+ public @interface SecurityType {}
+
/** Private constructor for Builder and Parcelable implementation. */
- private SoftApConfiguration(
- @Nullable String ssid, @Nullable MacAddress bssid, String wpa2Passphrase) {
+ private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
+ @Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
+ @SecurityType int securityType) {
mSsid = ssid;
mBssid = bssid;
mWpa2Passphrase = wpa2Passphrase;
+ mHiddenSsid = hiddenSsid;
+ mBand = band;
+ mChannel = channel;
+ mSecurityType = securityType;
}
@Override
@@ -80,12 +167,31 @@
SoftApConfiguration other = (SoftApConfiguration) otherObj;
return Objects.equals(mSsid, other.mSsid)
&& Objects.equals(mBssid, other.mBssid)
- && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase);
+ && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase)
+ && mHiddenSsid == other.mHiddenSsid
+ && mBand == other.mBand
+ && mChannel == other.mChannel
+ && mSecurityType == other.mSecurityType;
}
@Override
public int hashCode() {
- return Objects.hash(mSsid, mBssid, mWpa2Passphrase);
+ return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
+ mBand, mChannel, mSecurityType);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sbuf = new StringBuilder();
+ sbuf.append("ssid=").append(mSsid);
+ if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString());
+ sbuf.append(" \n Wpa2Passphrase =").append(
+ TextUtils.isEmpty(mWpa2Passphrase) ? "<empty>" : "<non-empty>");
+ sbuf.append(" \n HiddenSsid =").append(mHiddenSsid);
+ sbuf.append(" \n Band =").append(mBand);
+ sbuf.append(" \n Channel =").append(mChannel);
+ sbuf.append(" \n SecurityType=").append(getSecurityType());
+ return sbuf.toString();
}
@Override
@@ -93,6 +199,10 @@
dest.writeString(mSsid);
dest.writeParcelable(mBssid, flags);
dest.writeString(mWpa2Passphrase);
+ dest.writeBoolean(mHiddenSsid);
+ dest.writeInt(mBand);
+ dest.writeInt(mChannel);
+ dest.writeInt(mSecurityType);
}
@Override
@@ -107,7 +217,7 @@
return new SoftApConfiguration(
in.readString(),
in.readParcelable(MacAddress.class.getClassLoader()),
- in.readString());
+ in.readString(), in.readBoolean(), in.readInt(), in.readInt(), in.readInt());
}
@Override
@@ -116,22 +226,68 @@
}
};
+ /**
+ * Return String set to be the SSID for the AP.
+ * {@link #setSsid(String)}.
+ */
@Nullable
public String getSsid() {
return mSsid;
}
+ /**
+ * Returns MAC address set to be BSSID for the AP.
+ * {@link #setBssid(MacAddress)}.
+ */
@Nullable
public MacAddress getBssid() {
return mBssid;
}
+ /**
+ * Returns String set to be passphrase for the WPA2-PSK AP.
+ * {@link #setWpa2Passphrase(String)}.
+ */
@Nullable
public String getWpa2Passphrase() {
return mWpa2Passphrase;
}
/**
+ * Returns Boolean set to be indicate hidden (true: doesn't broadcast its SSID) or
+ * not (false: broadcasts its SSID) for the AP.
+ * {@link #setHiddenSsid(boolean)}.
+ */
+ public boolean isHiddenSsid() {
+ return mHiddenSsid;
+ }
+
+ /**
+ * Returns {@link BandType} set to be the band for the AP.
+ * {@link #setBand(@BandType int)}.
+ */
+ public @BandType int getBand() {
+ return mBand;
+ }
+
+ /**
+ * Returns Integer set to be the channel for the AP.
+ * {@link #setChannel(int)}.
+ */
+ public int getChannel() {
+ return mChannel;
+ }
+
+ /**
+ * Get security type params which depends on which security passphrase to set.
+ *
+ * @return One of the security types from {@link SecurityType}.
+ */
+ public @SecurityType int getSecurityType() {
+ return mSecurityType;
+ }
+
+ /**
* Builds a {@link SoftApConfiguration}, which allows an app to configure various aspects of a
* Soft AP.
*
@@ -142,6 +298,21 @@
private String mSsid;
private MacAddress mBssid;
private String mWpa2Passphrase;
+ private boolean mHiddenSsid;
+ private int mBand;
+ private int mChannel;
+
+ private int setSecurityType() {
+ int securityType = SECURITY_TYPE_OPEN;
+ if (!TextUtils.isEmpty(mWpa2Passphrase)) { // WPA2-PSK network.
+ securityType = SECURITY_TYPE_WPA2_PSK;
+ }
+ return securityType;
+ }
+
+ private void clearAllPassphrase() {
+ mWpa2Passphrase = null;
+ }
/**
* Constructs a Builder with default values (see {@link Builder}).
@@ -150,6 +321,9 @@
mSsid = null;
mBssid = null;
mWpa2Passphrase = null;
+ mHiddenSsid = false;
+ mBand = BAND_2GHZ;
+ mChannel = 0;
}
/**
@@ -161,6 +335,9 @@
mSsid = other.mSsid;
mBssid = other.mBssid;
mWpa2Passphrase = other.mWpa2Passphrase;
+ mHiddenSsid = other.mHiddenSsid;
+ mBand = other.mBand;
+ mChannel = other.mChannel;
}
/**
@@ -170,11 +347,16 @@
*/
@NonNull
public SoftApConfiguration build() {
- return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase);
+ return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
+ mHiddenSsid, mBand, mChannel, setSecurityType());
}
/**
* Specifies an SSID for the AP.
+ * <p>
+ * Null SSID only support when configure a local-only hotspot.
+ * <p>
+ * <li>If not set, defaults to null.</li>
*
* @param ssid SSID of valid Unicode characters, or null to have the SSID automatically
* chosen by the framework.
@@ -193,7 +375,10 @@
/**
* Specifies a BSSID for the AP.
- *
+ * <p>
+ * Only supported when configuring a local-only hotspot.
+ * <p>
+ * <li>If not set, defaults to null.</li>
* @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
* responsible for avoiding collisions.
* @return Builder for chaining.
@@ -211,8 +396,9 @@
}
/**
- * Specifies that this AP should use WPA2-PSK with the given passphrase. When set to null
- * and no other encryption method is configured, an open network is created.
+ * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
+ * When set to null, an open network is created.
+ * <p>
*
* @param passphrase The passphrase to use, or null to unset a previously-set WPA2-PSK
* configuration.
@@ -222,10 +408,72 @@
@NonNull
public Builder setWpa2Passphrase(@Nullable String passphrase) {
if (passphrase != null) {
+ final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
+ if (!asciiEncoder.canEncode(passphrase)) {
+ throw new IllegalArgumentException("passphrase not ASCII encodable");
+ }
Preconditions.checkStringNotEmpty(passphrase);
}
+ clearAllPassphrase();
mWpa2Passphrase = passphrase;
return this;
}
+
+ /**
+ * Specifies whether the AP is hidden (doesn't broadcast its SSID) or
+ * not (broadcasts its SSID).
+ * <p>
+ * <li>If not set, defaults to false (i.e not a hidden network).</li>
+ *
+ * @param hiddenSsid true for a hidden SSID, false otherwise.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setHiddenSsid(boolean hiddenSsid) {
+ mHiddenSsid = hiddenSsid;
+ return this;
+ }
+
+ /**
+ * Specifies the band for the AP.
+ * <p>
+ * <li>If not set, defaults to BAND_2GHZ {@link @BandType}.</li>
+ *
+ * @param band One of the band types from {@link @BandType}.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setBand(@BandType int band) {
+ switch (band) {
+ case BAND_2GHZ:
+ break;
+ case BAND_5GHZ:
+ break;
+ case BAND_ANY:
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid band type");
+ }
+ mBand = band;
+ return this;
+ }
+
+ /**
+ * Specifies the channel for the AP.
+ *
+ * The channel which AP resides on. Valid channels are country dependent.
+ * Use the special channel value 0 to have the framework auto-select a valid channel
+ * from the band configured with {@link #setBand(@BandType int)}.
+ *
+ * <p>
+ * <li>If not set, defaults to 0.</li>
+ * @param channel operating channel of the AP.
+ * @return Builder for chaining.
+ */
+ @NonNull
+ public Builder setChannel(int channel) {
+ mChannel = channel;
+ return this;
+ }
}
}
diff --git a/wifi/java/android/net/wifi/SynchronousExecutor.java b/wifi/java/android/net/wifi/SynchronousExecutor.java
new file mode 100644
index 0000000..9926b1b
--- /dev/null
+++ b/wifi/java/android/net/wifi/SynchronousExecutor.java
@@ -0,0 +1,29 @@
+/*
+ * 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.net.wifi;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An executor implementation that runs synchronously on the current thread.
+ * @hide
+ */
+public class SynchronousExecutor implements Executor {
+ @Override
+ public void execute(Runnable r) {
+ r.run();
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 0f7fc2d..f0f31fa 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -16,10 +16,14 @@
package android.net.wifi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* Record of energy and activity information from controller and
@@ -27,179 +31,197 @@
* real-time.
* @hide
*/
+@SystemApi
public final class WifiActivityEnergyInfo implements Parcelable {
- /**
- * @hide
- */
- public long mTimestamp;
+ private long mTimeSinceBootMillis;
+ @StackState
+ private int mStackState;
+ private long mControllerTxDurationMillis;
+ private long mControllerRxDurationMillis;
+ private long mControllerScanDurationMillis;
+ private long mControllerIdleDurationMillis;
+ private long mControllerEnergyUsedMicroJoules;
- /**
- * @hide
- */
- public int mStackState;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"STACK_STATE_"}, value = {
+ STACK_STATE_INVALID,
+ STACK_STATE_STATE_ACTIVE,
+ STACK_STATE_STATE_SCANNING,
+ STACK_STATE_STATE_IDLE})
+ public @interface StackState {}
- /**
- * @hide
- */
- public long mControllerTxTimeMs;
-
- /**
- * @hide
- */
- public long[] mControllerTxTimePerLevelMs;
-
- /**
- * @hide
- */
- public long mControllerRxTimeMs;
-
- /**
- * @hide
- */
- public long mControllerScanTimeMs;
-
- /**
- * @hide
- */
- public long mControllerIdleTimeMs;
-
- /**
- * @hide
- */
- public long mControllerEnergyUsed;
-
+ /** Invalid Wifi stack state. */
public static final int STACK_STATE_INVALID = 0;
+ /** Wifi stack is active. */
public static final int STACK_STATE_STATE_ACTIVE = 1;
+ /** Wifi stack is scanning. */
public static final int STACK_STATE_STATE_SCANNING = 2;
+ /** Wifi stack is idle. */
public static final int STACK_STATE_STATE_IDLE = 3;
- public WifiActivityEnergyInfo(long timestamp, int stackState,
- long txTime, long[] txTimePerLevel, long rxTime, long scanTime,
- long idleTime, long energyUsed) {
- mTimestamp = timestamp;
+ /**
+ * Constructor.
+ *
+ * @param timeSinceBootMillis the time since boot, in milliseconds.
+ * @param stackState The current state of the Wifi Stack. One of {@link #STACK_STATE_INVALID},
+ * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+ * or {@link #STACK_STATE_STATE_IDLE}.
+ * @param txDurationMillis Cumulative milliseconds of active transmission.
+ * @param rxDurationMillis Cumulative milliseconds of active receive.
+ * @param scanDurationMillis Cumulative milliseconds when radio is awake due to scan.
+ * @param idleDurationMillis Cumulative milliseconds when radio is awake but not transmitting or
+ * receiving.
+ * @param energyUsedMicroJoules Cumulative energy consumed by Wifi, in microjoules.
+ */
+ public WifiActivityEnergyInfo(
+ long timeSinceBootMillis,
+ @StackState int stackState,
+ long txDurationMillis,
+ long rxDurationMillis,
+ long scanDurationMillis,
+ long idleDurationMillis,
+ long energyUsedMicroJoules) {
+ mTimeSinceBootMillis = timeSinceBootMillis;
mStackState = stackState;
- mControllerTxTimeMs = txTime;
- mControllerTxTimePerLevelMs = txTimePerLevel;
- mControllerRxTimeMs = rxTime;
- mControllerScanTimeMs = scanTime;
- mControllerIdleTimeMs = idleTime;
- mControllerEnergyUsed = energyUsed;
+ mControllerTxDurationMillis = txDurationMillis;
+ mControllerRxDurationMillis = rxDurationMillis;
+ mControllerScanDurationMillis = scanDurationMillis;
+ mControllerIdleDurationMillis = idleDurationMillis;
+ mControllerEnergyUsedMicroJoules = energyUsedMicroJoules;
}
@Override
public String toString() {
return "WifiActivityEnergyInfo{"
- + " timestamp=" + mTimestamp
- + " mStackState=" + mStackState
- + " mControllerTxTimeMs=" + mControllerTxTimeMs
- + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
- + " mControllerRxTimeMs=" + mControllerRxTimeMs
- + " mControllerScanTimeMs=" + mControllerScanTimeMs
- + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
- + " mControllerEnergyUsed=" + mControllerEnergyUsed
- + " }";
+ + " mTimeSinceBootMillis=" + mTimeSinceBootMillis
+ + " mStackState=" + mStackState
+ + " mControllerTxDurationMillis=" + mControllerTxDurationMillis
+ + " mControllerRxDurationMillis=" + mControllerRxDurationMillis
+ + " mControllerScanDurationMillis=" + mControllerScanDurationMillis
+ + " mControllerIdleDurationMillis=" + mControllerIdleDurationMillis
+ + " mControllerEnergyUsedMicroJoules=" + mControllerEnergyUsedMicroJoules
+ + " }";
}
- public static final @android.annotation.NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR =
+ public static final @NonNull Parcelable.Creator<WifiActivityEnergyInfo> CREATOR =
new Parcelable.Creator<WifiActivityEnergyInfo>() {
public WifiActivityEnergyInfo createFromParcel(Parcel in) {
long timestamp = in.readLong();
int stackState = in.readInt();
long txTime = in.readLong();
- long[] txTimePerLevel = in.createLongArray();
long rxTime = in.readLong();
long scanTime = in.readLong();
long idleTime = in.readLong();
long energyUsed = in.readLong();
return new WifiActivityEnergyInfo(timestamp, stackState,
- txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed);
+ txTime, rxTime, scanTime, idleTime, energyUsed);
}
public WifiActivityEnergyInfo[] newArray(int size) {
return new WifiActivityEnergyInfo[size];
}
};
- public void writeToParcel(Parcel out, int flags) {
- out.writeLong(mTimestamp);
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeLong(mTimeSinceBootMillis);
out.writeInt(mStackState);
- out.writeLong(mControllerTxTimeMs);
- out.writeLongArray(mControllerTxTimePerLevelMs);
- out.writeLong(mControllerRxTimeMs);
- out.writeLong(mControllerScanTimeMs);
- out.writeLong(mControllerIdleTimeMs);
- out.writeLong(mControllerEnergyUsed);
+ out.writeLong(mControllerTxDurationMillis);
+ out.writeLong(mControllerRxDurationMillis);
+ out.writeLong(mControllerScanDurationMillis);
+ out.writeLong(mControllerIdleDurationMillis);
+ out.writeLong(mControllerEnergyUsedMicroJoules);
}
+ @Override
public int describeContents() {
return 0;
}
+ /** Get the timestamp (milliseconds since boot) of record creation. */
+ public long getTimeSinceBootMillis() {
+ return mTimeSinceBootMillis;
+ }
+
+ /** Set the timestamp (milliseconds since boot) of record creation. */
+ public void setTimeSinceBootMillis(long timeSinceBootMillis) {
+ mTimeSinceBootMillis = timeSinceBootMillis;
+ }
+
/**
- * @return bt stack reported state
+ * Get the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
+ * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+ * {@link #STACK_STATE_STATE_IDLE}.
*/
+ @StackState
public int getStackState() {
return mStackState;
}
/**
- * @return tx time in ms
+ * Set the Wifi stack reported state. One of {@link #STACK_STATE_INVALID},
+ * {@link #STACK_STATE_STATE_ACTIVE}, {@link #STACK_STATE_STATE_SCANNING},
+ * {@link #STACK_STATE_STATE_IDLE}.
*/
- public long getControllerTxTimeMillis() {
- return mControllerTxTimeMs;
+ public void setStackState(@StackState int stackState) {
+ mStackState = stackState;
}
- /**
- * @return tx time at power level provided in ms
- */
- public long getControllerTxTimeMillisAtLevel(int level) {
- if (level < mControllerTxTimePerLevelMs.length) {
- return mControllerTxTimePerLevelMs[level];
- }
- return 0;
+ /** Get the Wifi transmission duration, in milliseconds. */
+ public long getControllerTxDurationMillis() {
+ return mControllerTxDurationMillis;
}
- /**
- * @return rx time in ms
- */
- public long getControllerRxTimeMillis() {
- return mControllerRxTimeMs;
+ /** Set the Wifi transmission duration, in milliseconds. */
+ public void setControllerTxDurationMillis(long controllerTxDurationMillis) {
+ mControllerTxDurationMillis = controllerTxDurationMillis;
}
- /**
- * @return scan time in ms
- */
- public long getControllerScanTimeMillis() {
- return mControllerScanTimeMs;
+ /** Get the Wifi receive duration, in milliseconds. */
+ public long getControllerRxDurationMillis() {
+ return mControllerRxDurationMillis;
}
- /**
- * @return idle time in ms
- */
- public long getControllerIdleTimeMillis() {
- return mControllerIdleTimeMs;
+ /** Set the Wifi receive duration, in milliseconds. */
+ public void setControllerRxDurationMillis(long controllerRxDurationMillis) {
+ mControllerRxDurationMillis = controllerRxDurationMillis;
}
- /**
- * product of current(mA), voltage(V) and time(ms)
- * @return energy used
- */
- public long getControllerEnergyUsed() {
- return mControllerEnergyUsed;
- }
- /**
- * @return timestamp(wall clock) of record creation
- */
- public long getTimeStamp() {
- return mTimestamp;
+ /** Get the Wifi scan duration, in milliseconds. */
+ public long getControllerScanDurationMillis() {
+ return mControllerScanDurationMillis;
}
- /**
- * @return if the record is valid
- */
+ /** Set the Wifi scan duration, in milliseconds. */
+ public void setControllerScanDurationMillis(long controllerScanDurationMillis) {
+ mControllerScanDurationMillis = controllerScanDurationMillis;
+ }
+
+ /** Get the Wifi idle duration, in milliseconds. */
+ public long getControllerIdleDurationMillis() {
+ return mControllerIdleDurationMillis;
+ }
+
+ /** Set the Wifi idle duration, in milliseconds. */
+ public void setControllerIdleDurationMillis(long controllerIdleDurationMillis) {
+ mControllerIdleDurationMillis = controllerIdleDurationMillis;
+ }
+
+ /** Get the energy consumed by Wifi, in microjoules. */
+ public long getControllerEnergyUsedMicroJoules() {
+ return mControllerEnergyUsedMicroJoules;
+ }
+
+ /** Set the energy consumed by Wifi, in microjoules. */
+ public void setControllerEnergyUsedMicroJoules(long controllerEnergyUsedMicroJoules) {
+ mControllerEnergyUsedMicroJoules = controllerEnergyUsedMicroJoules;
+ }
+
+ /** Returns true if the record is valid, false otherwise. */
public boolean isValid() {
- return ((mControllerTxTimeMs >=0) &&
- (mControllerRxTimeMs >=0) &&
- (mControllerScanTimeMs >=0) &&
- (mControllerIdleTimeMs >=0));
+ return mControllerTxDurationMillis >= 0
+ && mControllerRxDurationMillis >= 0
+ && mControllerScanDurationMillis >= 0
+ && mControllerIdleDurationMillis >= 0;
}
-}
\ No newline at end of file
+}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e08a11e..d068fc6 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -39,7 +39,6 @@
import android.util.BackupUtils;
import android.util.Log;
import android.util.SparseArray;
-import android.util.TimeUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
@@ -49,6 +48,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.Calendar;
import java.util.HashMap;
/**
@@ -2006,6 +2006,16 @@
&& enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
}
+ private static String logTimeOfDay(long millis) {
+ Calendar c = Calendar.getInstance();
+ if (millis >= 0) {
+ c.setTimeInMillis(millis);
+ return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
+ } else {
+ return Long.toString(millis);
+ }
+ }
+
@Override
public String toString() {
StringBuilder sbuf = new StringBuilder();
@@ -2042,7 +2052,7 @@
if (mNetworkSelectionStatus.getConnectChoice() != null) {
sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
sbuf.append(" connect choice set time: ")
- .append(TimeUtils.logTimeOfDay(
+ .append(logTimeOfDay(
mNetworkSelectionStatus.getConnectChoiceTimestamp()));
}
sbuf.append(" hasEverConnected: ")
@@ -2081,7 +2091,7 @@
sbuf.append(" mRandomizedMacAddress: ").append(mRandomizedMacAddress).append("\n");
sbuf.append(" randomizedMacExpirationTimeMs: ")
.append(randomizedMacExpirationTimeMs == 0 ? "<none>"
- : TimeUtils.logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
+ : logTimeOfDay(randomizedMacExpirationTimeMs)).append("\n");
sbuf.append(" KeyMgmt:");
for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
if (this.allowedKeyManagement.get(k)) {
@@ -2205,7 +2215,7 @@
if (this.lastConnected != 0) {
sbuf.append('\n');
- sbuf.append("lastConnected: ").append(TimeUtils.logTimeOfDay(this.lastConnected));
+ sbuf.append("lastConnected: ").append(logTimeOfDay(this.lastConnected));
sbuf.append(" ");
}
sbuf.append('\n');
@@ -2355,7 +2365,7 @@
? getSsidAndSecurityTypeString()
: FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
if (!shared) {
- key += "-" + UserHandle.getUserId(creatorUid);
+ key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier();
}
return key;
}
@@ -2426,13 +2436,13 @@
@NonNull
@SystemApi
public IpConfiguration.IpAssignment getIpAssignment() {
- return mIpConfiguration.ipAssignment;
+ return mIpConfiguration.getIpAssignment();
}
/** @hide */
@UnsupportedAppUsage
public void setIpAssignment(IpConfiguration.IpAssignment ipAssignment) {
- mIpConfiguration.ipAssignment = ipAssignment;
+ mIpConfiguration.setIpAssignment(ipAssignment);
}
/**
@@ -2442,13 +2452,13 @@
@NonNull
@SystemApi
public IpConfiguration.ProxySettings getProxySettings() {
- return mIpConfiguration.proxySettings;
+ return mIpConfiguration.getProxySettings();
}
/** @hide */
@UnsupportedAppUsage
public void setProxySettings(IpConfiguration.ProxySettings proxySettings) {
- mIpConfiguration.proxySettings = proxySettings;
+ mIpConfiguration.setProxySettings(proxySettings);
}
/**
@@ -2457,10 +2467,10 @@
* WifiConfiguration, or {@code null} if no proxy is specified.
*/
public ProxyInfo getHttpProxy() {
- if (mIpConfiguration.proxySettings == IpConfiguration.ProxySettings.NONE) {
+ if (mIpConfiguration.getProxySettings() == IpConfiguration.ProxySettings.NONE) {
return null;
}
- return new ProxyInfo(mIpConfiguration.httpProxy);
+ return new ProxyInfo(mIpConfiguration.getHttpProxy());
}
/**
@@ -2485,12 +2495,12 @@
if (!Uri.EMPTY.equals(httpProxy.getPacFileUrl())) {
proxySettingCopy = IpConfiguration.ProxySettings.PAC;
// Construct a new PAC URL Proxy
- httpProxyCopy = new ProxyInfo(httpProxy.getPacFileUrl(), httpProxy.getPort());
+ httpProxyCopy = ProxyInfo.buildPacProxy(httpProxy.getPacFileUrl(), httpProxy.getPort());
} else {
proxySettingCopy = IpConfiguration.ProxySettings.STATIC;
// Construct a new HTTP Proxy
- httpProxyCopy = new ProxyInfo(httpProxy.getHost(), httpProxy.getPort(),
- httpProxy.getExclusionListAsString());
+ httpProxyCopy = ProxyInfo.buildDirectProxy(httpProxy.getHost(), httpProxy.getPort(),
+ Arrays.asList(httpProxy.getExclusionList()));
}
if (!httpProxyCopy.isValid()) {
throw new IllegalArgumentException("Invalid ProxyInfo: " + httpProxyCopy.toString());
@@ -2505,8 +2515,8 @@
*/
@SystemApi
public void setProxy(@NonNull ProxySettings settings, @NonNull ProxyInfo proxy) {
- mIpConfiguration.proxySettings = settings;
- mIpConfiguration.httpProxy = proxy;
+ mIpConfiguration.setProxySettings(settings);
+ mIpConfiguration.setHttpProxy(proxy);
}
/** Implement the Parcelable interface {@hide} */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6557bbe..e2e6728 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -36,15 +36,12 @@
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Binder;
-import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -57,7 +54,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.net.NetworkPinner;
import dalvik.system.CloseGuard;
@@ -2022,28 +2018,11 @@
*/
@Deprecated
public boolean enableNetwork(int netId, boolean attemptConnect) {
- final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP;
- if (pin) {
- NetworkRequest request = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build();
- NetworkPinner.pin(mContext, request);
- }
-
- boolean success;
try {
- success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
+ return mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
-
- if (pin && !success) {
- NetworkPinner.unpin();
- }
-
- return success;
}
/**
@@ -2760,7 +2739,6 @@
*
* @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STACK,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
@@ -2774,6 +2752,31 @@
}
/**
+ * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
+ * Note that starting Soft AP mode may disable station mode operation if the device does not
+ * support concurrency.
+ * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP,
+ * or null to use the persisted Soft AP configuration that was previously
+ * set using {@link #setSoftApConfiguration(softApConfiguration)}.
+ * @return {@code true} if the operation succeeded, {@code false} otherwise
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public boolean startTetheredHotspot(@Nullable SoftApConfiguration softApConfig) {
+ try {
+ return mService.startTetheredHotspot(softApConfig);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
* Stop SoftAp mode.
* Note that stopping softap mode will restore the previous wifi mode.
* @return {@code true} if the operation succeeds, {@code false} otherwise
@@ -3057,10 +3060,12 @@
* Gets the Wi-Fi AP Configuration.
* @return AP details in WifiConfiguration
*
+ * @deprecated This API is deprecated. Use {@link #getSoftApConfiguration()} instead.
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+ @Deprecated
public WifiConfiguration getWifiApConfiguration() {
try {
return mService.getWifiApConfiguration();
@@ -3070,17 +3075,56 @@
}
/**
- * Sets the Wi-Fi AP Configuration. The AP configuration must either be open or
- * WPA2 PSK networks.
+ * Gets the Wi-Fi AP Configuration.
+ * @return AP details in {@link SoftApConfiguration}
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
+ public SoftApConfiguration getSoftApConfiguration() {
+ try {
+ return mService.getSoftApConfiguration();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the Wi-Fi AP Configuration.
+ * @return {@code true} if the operation succeeded, {@code false} otherwise
+ *
+ * @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)}
+ * instead.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
+ @Deprecated
+ public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ try {
+ return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets the Wi-Fi AP Configuration.
+ *
+ * @param softApConfig A valid SoftApConfiguration specifying the configuration of the SAP.
+
* @return {@code true} if the operation succeeded, {@code false} otherwise
*
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
- public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public boolean setSoftApConfiguration(@NonNull SoftApConfiguration softApConfig) {
try {
- return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+ return mService.setSoftApConfiguration(
+ softApConfig, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -4569,6 +4613,36 @@
}
/**
+ * Retrieve the soft ap config data to be backed to save current config data.
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public byte[] retrieveSoftApBackupData() {
+ try {
+ return mService.retrieveSoftApBackupData();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Restore soft ap config from the backed up data.
+ * @hide
+ */
+ @Nullable
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void restoreSoftApBackupData(@NonNull byte[] data) {
+ try {
+ mService.restoreSoftApBackupData(data);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Restore state from the older version of back up data.
* The old backup data was essentially a backup of wpa_supplicant.conf
* and ipconfig.txt file.
@@ -5105,9 +5179,9 @@
Log.v(TAG, "OnWifiUsabilityStatsListener: "
+ "onWifiUsabilityStats: seqNum=" + seqNum);
}
- Binder.withCleanCallingIdentity(() ->
- executor.execute(() -> listener.onWifiUsabilityStats(seqNum,
- isSameBssidAndFreq, stats)));
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onWifiUsabilityStats(
+ seqNum, isSameBssidAndFreq, stats));
}
},
listener.hashCode()
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 246e96f..a5ca82c 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -116,6 +116,16 @@
*/
private int mCarrierId;
+ /**
+ * Whether this network is shared credential with user to allow user manually connect.
+ */
+ private boolean mIsUserAllowed;
+
+ /**
+ * Whether the setIsUserAllowedToManuallyConnect have been called.
+ */
+ private boolean mIsUserAllowedBeenSet;
+
public Builder() {
mSsid = null;
mBssid = null;
@@ -129,6 +139,8 @@
mIsAppInteractionRequired = false;
mIsUserInteractionRequired = false;
mIsMetered = false;
+ mIsUserAllowed = true;
+ mIsUserAllowedBeenSet = false;
mPriority = UNASSIGNED_PRIORITY;
mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
}
@@ -365,6 +377,27 @@
return this;
}
+ /**
+ * Specifies whether the network credentials provided with this suggestion can be used by
+ * the user to explicitly (manually) connect to this network. If true this network will
+ * appear in the Wi-Fi Picker (in Settings) and the user will be able to select and connect
+ * to it with the provided credentials. If false, the user will need to enter network
+ * credentials and the resulting configuration will become a user saved network.
+ * <p>
+ * <li>Note: Only valid for secure (non-open) networks.
+ * <li>If not set, defaults to true (i.e. allow user to manually connect) for secure
+ * networks and false for open networks.</li>
+ *
+ * @param isAllowed {@code true} to indicate that the credentials may be used by the user to
+ * manually connect to the network, {@code false} otherwise.
+ * @return Instance of {@link Builder} to enable chaining of the builder method.
+ */
+ public @NonNull Builder setIsUserAllowedToManuallyConnect(boolean isAllowed) {
+ mIsUserAllowed = isAllowed;
+ mIsUserAllowedBeenSet = true;
+ return this;
+ }
+
private void setSecurityParamsInWifiConfiguration(
@NonNull WifiConfiguration configuration) {
if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network.
@@ -516,6 +549,13 @@
throw new IllegalStateException("invalid bssid for suggestion");
}
wifiConfiguration = buildWifiConfiguration();
+ if (wifiConfiguration.isOpenNetwork()) {
+ if (mIsUserAllowedBeenSet && mIsUserAllowed) {
+ throw new IllegalStateException("Open network should not be "
+ + "setIsUserAllowedToManuallyConnect to true");
+ }
+ mIsUserAllowed = false;
+ }
}
return new WifiNetworkSuggestion(
@@ -523,6 +563,7 @@
mPasspointConfiguration,
mIsAppInteractionRequired,
mIsUserInteractionRequired,
+ mIsUserAllowed,
Process.myUid(),
ActivityThread.currentApplication().getApplicationContext().getOpPackageName());
}
@@ -564,12 +605,20 @@
*/
public final String suggestorPackageName;
+ /**
+ * Whether app share credential with the user, allow user use provided credential to
+ * connect network manually.
+ * @hide
+ */
+ public final boolean isUserAllowedToManuallyConnect;
+
/** @hide */
public WifiNetworkSuggestion() {
this.wifiConfiguration = null;
this.passpointConfiguration = null;
this.isAppInteractionRequired = false;
this.isUserInteractionRequired = false;
+ this.isUserAllowedToManuallyConnect = true;
this.suggestorUid = -1;
this.suggestorPackageName = null;
}
@@ -579,6 +628,7 @@
@Nullable PasspointConfiguration passpointConfiguration,
boolean isAppInteractionRequired,
boolean isUserInteractionRequired,
+ boolean isUserAllowedToManuallyConnect,
int suggestorUid, @NonNull String suggestorPackageName) {
checkNotNull(networkConfiguration);
checkNotNull(suggestorPackageName);
@@ -587,6 +637,7 @@
this.isAppInteractionRequired = isAppInteractionRequired;
this.isUserInteractionRequired = isUserInteractionRequired;
+ this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
this.suggestorUid = suggestorUid;
this.suggestorPackageName = suggestorPackageName;
}
@@ -600,6 +651,7 @@
in.readParcelable(null), // PasspointConfiguration
in.readBoolean(), // isAppInteractionRequired
in.readBoolean(), // isUserInteractionRequired
+ in.readBoolean(), // isSharedCredentialWithUser
in.readInt(), // suggestorUid
in.readString() // suggestorPackageName
);
@@ -622,6 +674,7 @@
dest.writeParcelable(passpointConfiguration, flags);
dest.writeBoolean(isAppInteractionRequired);
dest.writeBoolean(isUserInteractionRequired);
+ dest.writeBoolean(isUserAllowedToManuallyConnect);
dest.writeInt(suggestorUid);
dest.writeString(suggestorPackageName);
}
@@ -666,6 +719,7 @@
.append(", FQDN=").append(wifiConfiguration.FQDN)
.append(", isAppInteractionRequired=").append(isAppInteractionRequired)
.append(", isUserInteractionRequired=").append(isUserInteractionRequired)
+ .append(", isUserAllowedToManuallyConnect=").append(isUserAllowedToManuallyConnect)
.append(", suggestorUid=").append(suggestorUid)
.append(", suggestorPackageName=").append(suggestorPackageName)
.append("]");
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 0de5066..b293077 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -17,13 +17,16 @@
package android.net.wifi;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -45,6 +48,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* This class provides a way to scan the Wifi universe around the device
@@ -55,6 +59,60 @@
public class WifiScanner {
/** @hide */
+ public static final int WIFI_BAND_INDEX_24_GHZ = 0;
+ /** @hide */
+ public static final int WIFI_BAND_INDEX_5_GHZ = 1;
+ /** @hide */
+ public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2;
+ /** @hide */
+ public static final int WIFI_BAND_COUNT = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = {
+ WIFI_BAND_INDEX_24_GHZ,
+ WIFI_BAND_INDEX_5_GHZ,
+ WIFI_BAND_INDEX_5_GHZ_DFS_ONLY})
+ public @interface WifiBandIndex {}
+
+ /** no band specified; use channel list instead */
+ public static final int WIFI_BAND_UNSPECIFIED = 0;
+ /** 2.4 GHz band */
+ public static final int WIFI_BAND_24_GHZ = 1 << WIFI_BAND_INDEX_24_GHZ;
+ /** 5 GHz band excluding DFS channels */
+ public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ;
+ /** DFS channels from 5 GHz band only */
+ public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"WIFI_BAND_"}, value = {
+ WIFI_BAND_UNSPECIFIED,
+ WIFI_BAND_24_GHZ,
+ WIFI_BAND_5_GHZ,
+ WIFI_BAND_5_GHZ_DFS_ONLY})
+ public @interface WifiBandBasic {}
+
+ /**
+ * Combination of bands
+ * Note that those are only the common band combinations,
+ * other combinations can be created by combining any of the basic bands above
+ */
+ /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
+ public static final int WIFI_BAND_BOTH = WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ;
+ /**
+ * 2.4Ghz band + DFS channels from 5 GHz band only
+ * @hide
+ */
+ public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS =
+ WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+ /** 5 GHz band including DFS channels */
+ public static final int WIFI_BAND_5_GHZ_WITH_DFS = WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+ /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
+ public static final int WIFI_BAND_BOTH_WITH_DFS =
+ WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"WIFI_BAND_"}, value = {
WIFI_BAND_UNSPECIFIED,
@@ -67,25 +125,6 @@
WIFI_BAND_BOTH_WITH_DFS})
public @interface WifiBand {}
- /** no band specified; use channel list instead */
- public static final int WIFI_BAND_UNSPECIFIED = 0;
- /** 2.4 GHz band */
- public static final int WIFI_BAND_24_GHZ = 1;
- /** 5 GHz band excluding DFS channels */
- public static final int WIFI_BAND_5_GHZ = 2;
- /** Both 2.4 GHz band and 5 GHz band; no DFS channels */
- public static final int WIFI_BAND_BOTH = 3;
- /** DFS channels from 5 GHz band only */
- public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4;
- /**
- * 2.4Ghz band + DFS channels from 5 GHz band only
- * @hide
- */
- public static final int WIFI_BAND_24_GHZ_WITH_5GHZ_DFS = 5;
- /** 5 GHz band including DFS channels */
- public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6;
- /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
- public static final int WIFI_BAND_BOTH_WITH_DFS = 7;
/**
* Max band value
* @hide
@@ -196,24 +235,29 @@
*/
public static final int REPORT_EVENT_NO_BATCH = (1 << 2);
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"SCAN_TYPE_"}, value = {
+ SCAN_TYPE_LOW_LATENCY,
+ SCAN_TYPE_LOW_POWER,
+ SCAN_TYPE_HIGH_ACCURACY})
+ public @interface ScanType {}
+
/**
- * This is used to indicate the purpose of the scan to the wifi chip in
- * {@link ScanSettings#type}.
- * On devices with multiple hardware radio chains (and hence different modes of scan),
- * this type serves as an indication to the hardware on what mode of scan to perform.
- * Only apps holding android.Manifest.permission.NETWORK_STACK permission can set this value.
- *
- * Note: This serves as an intent and not as a stipulation, the wifi chip
- * might honor or ignore the indication based on the current radio conditions. Always
- * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration used
- * to receive the corresponding scan result.
+ * Optimize the scan for lower latency.
+ * @see ScanSettings#type
*/
- /** {@hide} */
- public static final int TYPE_LOW_LATENCY = 0;
- /** {@hide} */
- public static final int TYPE_LOW_POWER = 1;
- /** {@hide} */
- public static final int TYPE_HIGH_ACCURACY = 2;
+ public static final int SCAN_TYPE_LOW_LATENCY = 0;
+ /**
+ * Optimize the scan for lower power usage.
+ * @see ScanSettings#type
+ */
+ public static final int SCAN_TYPE_LOW_POWER = 1;
+ /**
+ * Optimize the scan for higher accuracy.
+ * @see ScanSettings#type
+ */
+ public static final int SCAN_TYPE_HIGH_ACCURACY = 2;
/** {@hide} */
public static final String SCAN_PARAMS_SCAN_SETTINGS_KEY = "ScanSettings";
@@ -228,18 +272,14 @@
* scan configuration parameters to be sent to {@link #startBackgroundScan}
*/
public static class ScanSettings implements Parcelable {
- /**
- * Hidden network to be scanned for.
- * {@hide}
- */
+ /** Hidden network to be scanned for. */
public static class HiddenNetwork {
/** SSID of the network */
- public String ssid;
+ @NonNull
+ public final String ssid;
- /**
- * Default constructor for HiddenNetwork.
- */
- public HiddenNetwork(String ssid) {
+ /** Default constructor for HiddenNetwork. */
+ public HiddenNetwork(@NonNull String ssid) {
this.ssid = ssid;
}
}
@@ -249,12 +289,12 @@
/** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
public ChannelSpec[] channels;
/**
- * list of hidden networks to scan for. Explicit probe requests are sent out for such
+ * List of hidden networks to scan for. Explicit probe requests are sent out for such
* networks during scan. Only valid for single scan requests.
- * {@hide}
*/
+ @NonNull
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
- public HiddenNetwork[] hiddenNetworks;
+ public final List<HiddenNetwork> hiddenNetworks = new ArrayList<>();
/** period of background scan; in millisecond, 0 => single shot scan */
public int periodInMs;
/** must have a valid REPORT_EVENT value */
@@ -285,11 +325,24 @@
public boolean isPnoScan;
/**
* Indicate the type of scan to be performed by the wifi chip.
- * Default value: {@link #TYPE_LOW_LATENCY}.
- * {@hide}
+ *
+ * On devices with multiple hardware radio chains (and hence different modes of scan),
+ * this type serves as an indication to the hardware on what mode of scan to perform.
+ * Only apps holding {@link android.Manifest.permission.NETWORK_STACK} permission can set
+ * this value.
+ *
+ * Note: This serves as an intent and not as a stipulation, the wifi chip
+ * might honor or ignore the indication based on the current radio conditions. Always
+ * use the {@link ScanResult#radioChainInfos} to figure out the radio chain configuration
+ * used to receive the corresponding scan result.
+ *
+ * One of {@link #SCAN_TYPE_LOW_LATENCY}, {@link #SCAN_TYPE_LOW_POWER},
+ * {@link #SCAN_TYPE_HIGH_ACCURACY}.
+ * Default value: {@link #SCAN_TYPE_LOW_LATENCY}.
*/
+ @ScanType
@RequiresPermission(android.Manifest.permission.NETWORK_STACK)
- public int type = TYPE_LOW_LATENCY;
+ public int type = SCAN_TYPE_LOW_LATENCY;
/**
* This scan request may ignore location settings while receiving scans. This should only
* be used in emergency situations.
@@ -336,13 +389,9 @@
} else {
dest.writeInt(0);
}
- if (hiddenNetworks != null) {
- dest.writeInt(hiddenNetworks.length);
- for (int i = 0; i < hiddenNetworks.length; i++) {
- dest.writeString(hiddenNetworks[i].ssid);
- }
- } else {
- dest.writeInt(0);
+ dest.writeInt(hiddenNetworks.size());
+ for (HiddenNetwork hiddenNetwork : hiddenNetworks) {
+ dest.writeString(hiddenNetwork.ssid);
}
}
@@ -372,10 +421,10 @@
settings.channels[i] = spec;
}
int numNetworks = in.readInt();
- settings.hiddenNetworks = new HiddenNetwork[numNetworks];
+ settings.hiddenNetworks.clear();
for (int i = 0; i < numNetworks; i++) {
String ssid = in.readString();
- settings.hiddenNetworks[i] = new HiddenNetwork(ssid);;
+ settings.hiddenNetworks.add(new HiddenNetwork(ssid));
}
return settings;
}
@@ -384,7 +433,6 @@
return new ScanSettings[size];
}
};
-
}
/**
@@ -801,33 +849,44 @@
}
/**
- * Register a listener that will receive results from all single scans
- * Either the onSuccess/onFailure will be called once when the listener is registered. After
- * (assuming onSuccess was called) all subsequent single scan results will be delivered to the
- * listener. It is possible that onFullResult will not be called for all results of the first
- * scan if the listener was registered during the scan.
+ * Register a listener that will receive results from all single scans.
+ * Either the {@link ScanListener#onSuccess()} or {@link ScanListener#onFailure(int, String)}
+ * method will be called once when the listener is registered.
+ * Afterwards (assuming onSuccess was called), all subsequent single scan results will be
+ * delivered to the listener. It is possible that onFullResult will not be called for all
+ * results of the first scan if the listener was registered during the scan.
*
* @param listener specifies the object to report events to. This object is also treated as a
* key for this request, and must also be specified to cancel the request.
* Multiple requests should also not share this object.
- * {@hide}
*/
@RequiresPermission(Manifest.permission.NETWORK_STACK)
- public void registerScanListener(ScanListener listener) {
+ public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull ScanListener listener) {
+ Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(listener, "listener cannot be null");
- int key = addListener(listener);
+ int key = addListener(listener, executor);
if (key == INVALID_KEY) return;
validateChannel();
mAsyncChannel.sendMessage(CMD_REGISTER_SCAN_LISTENER, 0, key);
}
/**
+ * Overload of {@link #registerScanListener(Executor, ScanListener)} that executes the callback
+ * synchronously.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.NETWORK_STACK)
+ public void registerScanListener(@NonNull ScanListener listener) {
+ registerScanListener(new SynchronousExecutor(), listener);
+ }
+
+ /**
* Deregister a listener for ongoing single scans
* @param listener specifies which scan to cancel; must be same object as passed in {@link
* #registerScanListener}
- * {@hide}
*/
- public void deregisterScanListener(ScanListener listener) {
+ public void unregisterScanListener(@NonNull ScanListener listener) {
Preconditions.checkNotNull(listener, "listener cannot be null");
int key = removeListener(listener);
if (key == INVALID_KEY) return;
@@ -1280,6 +1339,7 @@
private int mListenerKey = 1;
private final SparseArray mListenerMap = new SparseArray();
+ private final SparseArray<Executor> mExecutorMap = new SparseArray<>();
private final Object mListenerMapLock = new Object();
private AsyncChannel mAsyncChannel;
@@ -1327,10 +1387,14 @@
"No permission to access and change wifi or a bad initialization");
}
+ private int addListener(ActionListener listener) {
+ return addListener(listener, null);
+ }
+
// Add a listener into listener map. If the listener already exists, return INVALID_KEY and
// send an error message to internal handler; Otherwise add the listener to the listener map and
// return the key of the listener.
- private int addListener(ActionListener listener) {
+ private int addListener(ActionListener listener, Executor executor) {
synchronized (mListenerMapLock) {
boolean keyExists = (getListenerKey(listener) != INVALID_KEY);
// Note we need to put the listener into listener map even if it's a duplicate as the
@@ -1346,6 +1410,7 @@
message.sendToTarget();
return INVALID_KEY;
} else {
+ mExecutorMap.put(key, executor);
return key;
}
}
@@ -1363,11 +1428,22 @@
return key;
}
- private Object getListener(int key) {
- if (key == INVALID_KEY) return null;
+ private static class ListenerWithExecutor {
+ @Nullable final Object mListener;
+ @Nullable final Executor mExecutor;
+
+ ListenerWithExecutor(@Nullable Object listener, @Nullable Executor executor) {
+ mListener = listener;
+ mExecutor = executor;
+ }
+ }
+
+ private ListenerWithExecutor getListenerWithExecutor(int key) {
+ if (key == INVALID_KEY) return new ListenerWithExecutor(null, null);
synchronized (mListenerMapLock) {
Object listener = mListenerMap.get(key);
- return listener;
+ Executor executor = mExecutorMap.get(key);
+ return new ListenerWithExecutor(listener, executor);
}
}
@@ -1388,6 +1464,7 @@
synchronized (mListenerMapLock) {
Object listener = mListenerMap.get(key);
mListenerMap.remove(key);
+ mExecutorMap.remove(key);
return listener;
}
}
@@ -1400,6 +1477,7 @@
}
synchronized (mListenerMapLock) {
mListenerMap.remove(key);
+ mExecutorMap.remove(key);
return key;
}
}
@@ -1458,7 +1536,8 @@
return;
}
- Object listener = getListener(msg.arg2);
+ ListenerWithExecutor listenerWithExecutor = getListenerWithExecutor(msg.arg2);
+ Object listener = listenerWithExecutor.mListener;
if (listener == null) {
if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
@@ -1467,36 +1546,52 @@
if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
}
+ Executor executor = listenerWithExecutor.mExecutor;
+ if (executor == null) {
+ executor = new SynchronousExecutor();
+ }
+
switch (msg.what) {
- /* ActionListeners grouped together */
- case CMD_OP_SUCCEEDED :
- ((ActionListener) listener).onSuccess();
- break;
- case CMD_OP_FAILED : {
- OperationResult result = (OperationResult)msg.obj;
- ((ActionListener) listener).onFailure(result.reason, result.description);
- removeListener(msg.arg2);
- }
- break;
- case CMD_SCAN_RESULT :
- ((ScanListener) listener).onResults(
- ((ParcelableScanData) msg.obj).getResults());
- return;
- case CMD_FULL_SCAN_RESULT :
+ /* ActionListeners grouped together */
+ case CMD_OP_SUCCEEDED: {
+ ActionListener actionListener = (ActionListener) listener;
+ Binder.clearCallingIdentity();
+ executor.execute(actionListener::onSuccess);
+ } break;
+ case CMD_OP_FAILED: {
+ OperationResult result = (OperationResult) msg.obj;
+ ActionListener actionListener = (ActionListener) listener;
+ removeListener(msg.arg2);
+ Binder.clearCallingIdentity();
+ executor.execute(() ->
+ actionListener.onFailure(result.reason, result.description));
+ } break;
+ case CMD_SCAN_RESULT: {
+ ScanListener scanListener = (ScanListener) listener;
+ ParcelableScanData parcelableScanData = (ParcelableScanData) msg.obj;
+ Binder.clearCallingIdentity();
+ executor.execute(() -> scanListener.onResults(parcelableScanData.getResults()));
+ } break;
+ case CMD_FULL_SCAN_RESULT: {
ScanResult result = (ScanResult) msg.obj;
- ((ScanListener) listener).onFullResult(result);
- return;
- case CMD_SINGLE_SCAN_COMPLETED:
+ ScanListener scanListener = ((ScanListener) listener);
+ Binder.clearCallingIdentity();
+ executor.execute(() -> scanListener.onFullResult(result));
+ } break;
+ case CMD_SINGLE_SCAN_COMPLETED: {
if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
- break;
- case CMD_PNO_NETWORK_FOUND:
- ((PnoScanListener) listener).onPnoNetworkFound(
- ((ParcelableScanResults) msg.obj).getResults());
- return;
- default:
+ } break;
+ case CMD_PNO_NETWORK_FOUND: {
+ PnoScanListener pnoScanListener = (PnoScanListener) listener;
+ ParcelableScanResults parcelableScanResults = (ParcelableScanResults) msg.obj;
+ Binder.clearCallingIdentity();
+ executor.execute(() ->
+ pnoScanListener.onPnoNetworkFound(parcelableScanResults.getResults()));
+ } break;
+ default: {
if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
- return;
+ } break;
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index b3b5b29..2d3cc1e 100644
--- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -18,8 +18,6 @@
import android.annotation.Nullable;
-import libcore.io.Memory;
-
import java.nio.BufferOverflowException;
import java.nio.ByteOrder;
import java.util.ArrayList;
@@ -266,7 +264,7 @@
public TlvConstructor putShort(int type, short data) {
checkLength(2);
addHeader(type, 2);
- Memory.pokeShort(mArray, mPosition, data, mByteOrder);
+ pokeShort(mArray, mPosition, data, mByteOrder);
mPosition += 2;
return this;
}
@@ -284,7 +282,7 @@
public TlvConstructor putInt(int type, int data) {
checkLength(4);
addHeader(type, 4);
- Memory.pokeInt(mArray, mPosition, data, mByteOrder);
+ pokeInt(mArray, mPosition, data, mByteOrder);
mPosition += 4;
return this;
}
@@ -349,14 +347,14 @@
if (mTypeSize == 1) {
mArray[mPosition] = (byte) type;
} else if (mTypeSize == 2) {
- Memory.pokeShort(mArray, mPosition, (short) type, mByteOrder);
+ pokeShort(mArray, mPosition, (short) type, mByteOrder);
}
mPosition += mTypeSize;
if (mLengthSize == 1) {
mArray[mPosition] = (byte) length;
} else if (mLengthSize == 2) {
- Memory.pokeShort(mArray, mPosition, (short) length, mByteOrder);
+ pokeShort(mArray, mPosition, (short) length, mByteOrder);
}
mPosition += mLengthSize;
}
@@ -445,7 +443,7 @@
throw new IllegalArgumentException(
"Accesing a short from a TLV element of length " + length);
}
- return Memory.peekShort(mRefArray, offset, byteOrder);
+ return peekShort(mRefArray, offset, byteOrder);
}
/**
@@ -460,7 +458,7 @@
throw new IllegalArgumentException(
"Accesing an int from a TLV element of length " + length);
}
- return Memory.peekInt(mRefArray, offset, byteOrder);
+ return peekInt(mRefArray, offset, byteOrder);
}
/**
@@ -590,7 +588,7 @@
if (mTypeSize == 1) {
type = mArray[mOffset];
} else if (mTypeSize == 2) {
- type = Memory.peekShort(mArray, mOffset, mByteOrder);
+ type = peekShort(mArray, mOffset, mByteOrder);
}
mOffset += mTypeSize;
@@ -598,7 +596,7 @@
if (mLengthSize == 1) {
length = mArray[mOffset];
} else if (mLengthSize == 2) {
- length = Memory.peekShort(mArray, mOffset, mByteOrder);
+ length = peekShort(mArray, mOffset, mByteOrder);
}
mOffset += mLengthSize;
@@ -661,10 +659,56 @@
if (lengthSize == 1) {
nextTlvIndex += lengthSize + array[nextTlvIndex];
} else {
- nextTlvIndex += lengthSize + Memory.peekShort(array, nextTlvIndex, byteOrder);
+ nextTlvIndex += lengthSize + peekShort(array, nextTlvIndex, byteOrder);
}
}
return nextTlvIndex == array.length;
}
+
+ private static void pokeShort(byte[] dst, int offset, short value, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ dst[offset++] = (byte) ((value >> 8) & 0xff);
+ dst[offset ] = (byte) ((value >> 0) & 0xff);
+ } else {
+ dst[offset++] = (byte) ((value >> 0) & 0xff);
+ dst[offset ] = (byte) ((value >> 8) & 0xff);
+ }
+ }
+
+ private static void pokeInt(byte[] dst, int offset, int value, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ dst[offset++] = (byte) ((value >> 24) & 0xff);
+ dst[offset++] = (byte) ((value >> 16) & 0xff);
+ dst[offset++] = (byte) ((value >> 8) & 0xff);
+ dst[offset ] = (byte) ((value >> 0) & 0xff);
+ } else {
+ dst[offset++] = (byte) ((value >> 0) & 0xff);
+ dst[offset++] = (byte) ((value >> 8) & 0xff);
+ dst[offset++] = (byte) ((value >> 16) & 0xff);
+ dst[offset ] = (byte) ((value >> 24) & 0xff);
+ }
+ }
+
+ private static short peekShort(byte[] src, int offset, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ return (short) ((src[offset] << 8) | (src[offset + 1] & 0xff));
+ } else {
+ return (short) ((src[offset + 1] << 8) | (src[offset] & 0xff));
+ }
+ }
+
+ private static int peekInt(byte[] src, int offset, ByteOrder order) {
+ if (order == ByteOrder.BIG_ENDIAN) {
+ return ((src[offset++] & 0xff) << 24)
+ | ((src[offset++] & 0xff) << 16)
+ | ((src[offset++] & 0xff) << 8)
+ | ((src[offset ] & 0xff) << 0);
+ } else {
+ return ((src[offset++] & 0xff) << 0)
+ | ((src[offset++] & 0xff) << 8)
+ | ((src[offset++] & 0xff) << 16)
+ | ((src[offset ] & 0xff) << 24);
+ }
+ }
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 9164d04..5ec4c8b 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -207,14 +207,14 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeBlob(mData);
+ dest.writeByteArray(mData);
}
public static final @android.annotation.NonNull Creator<ByteArrayWrapper> CREATOR =
new Creator<ByteArrayWrapper>() {
@Override
public ByteArrayWrapper createFromParcel(Parcel in) {
- return new ByteArrayWrapper(in.readBlob());
+ return new ByteArrayWrapper(in.createByteArray());
}
@Override
diff --git a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
index 3bef502..f0a0607 100644
--- a/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
+++ b/wifi/java/android/net/wifi/hotspot2/OsuProvider.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiSsid;
import android.os.Bundle;
@@ -86,23 +85,16 @@
*/
private final List<Integer> mMethodList;
- /**
- * Icon data for the OSU (Online Sign-Up) provider.
- */
- private final Icon mIcon;
-
/** @hide */
public OsuProvider(String osuSsid, Map<String, String> friendlyNames,
- String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
- Icon icon) {
+ String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) {
this(WifiSsid.createFromByteArray(osuSsid.getBytes(StandardCharsets.UTF_8)),
- friendlyNames, serviceDescription, serverUri, nai, methodList, icon);
+ friendlyNames, serviceDescription, serverUri, nai, methodList);
}
/** @hide */
public OsuProvider(WifiSsid osuSsid, Map<String, String> friendlyNames,
- String serviceDescription, Uri serverUri, String nai, List<Integer> methodList,
- Icon icon) {
+ String serviceDescription, Uri serverUri, String nai, List<Integer> methodList) {
mOsuSsid = osuSsid;
mFriendlyNames = friendlyNames;
mServiceDescription = serviceDescription;
@@ -113,7 +105,6 @@
} else {
mMethodList = new ArrayList<>(methodList);
}
- mIcon = icon;
}
/**
@@ -130,7 +121,6 @@
mServerUri = null;
mNetworkAccessIdentifier = null;
mMethodList = new ArrayList<>();
- mIcon = null;
return;
}
@@ -144,7 +134,6 @@
} else {
mMethodList = new ArrayList<>(source.mMethodList);
}
- mIcon = source.mIcon;
}
/** @hide */
@@ -205,11 +194,6 @@
return mMethodList;
}
- /** @hide */
- public Icon getIcon() {
- return mIcon;
- }
-
@Override
public int describeContents() {
return 0;
@@ -222,7 +206,6 @@
dest.writeParcelable(mServerUri, flags);
dest.writeString(mNetworkAccessIdentifier);
dest.writeList(mMethodList);
- dest.writeParcelable(mIcon, flags);
Bundle bundle = new Bundle();
bundle.putSerializable("friendlyNameMap", (HashMap<String, String>) mFriendlyNames);
dest.writeBundle(bundle);
@@ -237,21 +220,16 @@
return false;
}
OsuProvider that = (OsuProvider) thatObject;
- return (mOsuSsid == null ? that.mOsuSsid == null : mOsuSsid.equals(that.mOsuSsid))
- && (mFriendlyNames == null) ? that.mFriendlyNames == null
- : mFriendlyNames.equals(that.mFriendlyNames)
+ return Objects.equals(mOsuSsid, that.mOsuSsid)
+ && Objects.equals(mFriendlyNames, that.mFriendlyNames)
&& TextUtils.equals(mServiceDescription, that.mServiceDescription)
- && (mServerUri == null ? that.mServerUri == null
- : mServerUri.equals(that.mServerUri))
+ && Objects.equals(mServerUri, that.mServerUri)
&& TextUtils.equals(mNetworkAccessIdentifier, that.mNetworkAccessIdentifier)
- && (mMethodList == null ? that.mMethodList == null
- : mMethodList.equals(that.mMethodList))
- && (mIcon == null ? that.mIcon == null : mIcon.sameAs(that.mIcon));
+ && Objects.equals(mMethodList, that.mMethodList);
}
@Override
public int hashCode() {
- // mIcon is not hashable, skip the variable.
return Objects.hash(mOsuSsid, mServiceDescription, mFriendlyNames,
mServerUri, mNetworkAccessIdentifier, mMethodList);
}
@@ -264,8 +242,7 @@
+ " mServiceDescription=" + mServiceDescription
+ " mServerUri=" + mServerUri
+ " mNetworkAccessIdentifier=" + mNetworkAccessIdentifier
- + " mMethodList=" + mMethodList
- + " mIcon=" + mIcon;
+ + " mMethodList=" + mMethodList;
}
public static final @android.annotation.NonNull Creator<OsuProvider> CREATOR =
@@ -278,12 +255,11 @@
String nai = in.readString();
List<Integer> methodList = new ArrayList<>();
in.readList(methodList, null);
- Icon icon = in.readParcelable(null);
Bundle bundle = in.readBundle();
Map<String, String> friendlyNamesMap = (HashMap) bundle.getSerializable(
"friendlyNameMap");
return new OsuProvider(osuSsid, friendlyNamesMap, serviceDescription,
- serverUri, nai, methodList, icon);
+ serverUri, nai, methodList);
}
@Override
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 13b2520..98ec208 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -286,14 +286,12 @@
}
/**
- * Update device details. This will throw an exception if the device address does not match.
+ * Update this device's details using another {@link WifiP2pDevice} instance.
+ * This will throw an exception if the device address does not match.
*
- * @param device to be updated
+ * @param device another instance of {@link WifiP2pDevice} used to update this instance.
* @throws IllegalArgumentException if the device is null or the device address does not match
- *
- * @hide
*/
- @UnsupportedAppUsage
public void update(@NonNull WifiP2pDevice device) {
updateSupplicantDetails(device);
status = device.status;
diff --git a/wifi/java/android/net/wifi/rtt/package.html b/wifi/java/android/net/wifi/rtt/package.html
index e639282..4a32f52 100644
--- a/wifi/java/android/net/wifi/rtt/package.html
+++ b/wifi/java/android/net/wifi/rtt/package.html
@@ -37,5 +37,9 @@
<pre>
getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
</pre>
+
+<p>For an example of this functionality, see
+<a href="{@docRoot}guide/topics/connectivity/wifi-rtt" class="external">Wi-Fi location: ranging
+with RTT</a>.</p>
</BODY>
</HTML>
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index 534e609..bc86839 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -285,6 +285,11 @@
}
@Override
+ public boolean startTetheredHotspot(SoftApConfiguration softApConfig) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean stopSoftAp() {
throw new UnsupportedOperationException();
}
@@ -321,10 +326,21 @@
}
@Override
+ public SoftApConfiguration getSoftApConfiguration() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) {
throw new UnsupportedOperationException();
}
+ @Override
+ public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void notifyUserOfApBandConversion(String packageName) {
throw new UnsupportedOperationException();
}
@@ -385,6 +401,16 @@
}
@Override
+ public byte[] retrieveSoftApBackupData() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void restoreSoftApBackupData(byte[] data) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 949b479..b8d3e41 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -46,6 +46,10 @@
assertThat(original.getSsid()).isEqualTo("ssid");
assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
assertThat(original.getWpa2Passphrase()).isNull();
+ assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
+ assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
+ assertThat(original.getChannel()).isEqualTo(0);
+ assertThat(original.isHiddenSsid()).isEqualTo(false);
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
@@ -64,6 +68,39 @@
.setWpa2Passphrase("secretsecret")
.build();
assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+ assertThat(original.getSecurityType()).isEqualTo(
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+ assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
+ assertThat(original.getChannel()).isEqualTo(0);
+ assertThat(original.isHiddenSsid()).isEqualTo(false);
+
+
+ SoftApConfiguration unparceled = parcelUnparcel(original);
+ assertThat(unparceled).isNotSameAs(original);
+ assertThat(unparceled).isEqualTo(original);
+ assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+ SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+ assertThat(copy).isNotSameAs(original);
+ assertThat(copy).isEqualTo(original);
+ assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+ }
+
+ @Test
+ public void testWpa2WithBandAndChannelAndHiddenNetwork() {
+ SoftApConfiguration original = new SoftApConfiguration.Builder()
+ .setWpa2Passphrase("secretsecret")
+ .setBand(SoftApConfiguration.BAND_ANY)
+ .setChannel(149)
+ .setHiddenSsid(true)
+ .build();
+ assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+ assertThat(original.getSecurityType()).isEqualTo(
+ SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
+ assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_ANY);
+ assertThat(original.getChannel()).isEqualTo(149);
+ assertThat(original.isHiddenSsid()).isEqualTo(true);
+
SoftApConfiguration unparceled = parcelUnparcel(original);
assertThat(unparceled).isNotSameAs(original);
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 8cdcba6..d22dbd3 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -181,6 +181,33 @@
}
/**
+ * Check the call to startSoftAp calls WifiService to startSoftAp with the provided
+ * WifiConfiguration. Verify that the return value is propagated to the caller.
+ */
+ @Test
+ public void testStartTetheredHotspotCallsServiceWithSoftApConfig() throws Exception {
+ SoftApConfiguration mSoftApConfig = new SoftApConfiguration.Builder().build();
+ when(mWifiService.startTetheredHotspot(eq(mSoftApConfig))).thenReturn(true);
+ assertTrue(mWifiManager.startTetheredHotspot(mSoftApConfig));
+
+ when(mWifiService.startTetheredHotspot(eq(mSoftApConfig))).thenReturn(false);
+ assertFalse(mWifiManager.startTetheredHotspot(mSoftApConfig));
+ }
+
+ /**
+ * Check the call to startSoftAp calls WifiService to startSoftAp with a null config. Verify
+ * that the return value is propagated to the caller.
+ */
+ @Test
+ public void testStartTetheredHotspotCallsServiceWithNullConfig() throws Exception {
+ when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(true);
+ assertTrue(mWifiManager.startTetheredHotspot(null));
+
+ when(mWifiService.startTetheredHotspot(eq(null))).thenReturn(false);
+ assertFalse(mWifiManager.startTetheredHotspot(null));
+ }
+
+ /**
* Test creation of a LocalOnlyHotspotReservation and verify that close properly calls
* WifiService.stopLocalOnlyHotspot.
*/
@@ -1148,6 +1175,43 @@
}
/**
+ * Verify that a successful call properly returns true.
+ */
+ @Test
+ public void testSetSoftApConfigurationSuccessReturnsTrue() throws Exception {
+ SoftApConfiguration apConfig = new SoftApConfiguration.Builder().build();
+
+ when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+ .thenReturn(true);
+ assertTrue(mWifiManager.setSoftApConfiguration(apConfig));
+ }
+
+ /**
+ * Verify that a failed call properly returns false.
+ */
+ @Test
+ public void testSetSoftApConfigurationFailureReturnsFalse() throws Exception {
+ SoftApConfiguration apConfig = new SoftApConfiguration.Builder().build();
+
+ when(mWifiService.setSoftApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+ .thenReturn(false);
+ assertFalse(mWifiManager.setSoftApConfiguration(apConfig));
+ }
+
+ /**
+ * Verify Exceptions are rethrown when underlying calls to WifiService throw exceptions.
+ */
+ @Test
+ public void testSetSoftApConfigurationRethrowsException() throws Exception {
+ doThrow(new SecurityException()).when(mWifiService).setSoftApConfiguration(any(), any());
+
+ try {
+ mWifiManager.setSoftApConfiguration(new SoftApConfiguration.Builder().build());
+ fail("setWifiApConfiguration should rethrow Exceptions from WifiService");
+ } catch (SecurityException e) { }
+ }
+
+ /**
* Check the call to startScan calls WifiService.
*/
@Test
@@ -1437,15 +1501,6 @@
}
/**
- * Defined for testing purpose.
- */
- class SynchronousExecutor implements Executor {
- public void execute(Runnable r) {
- r.run();
- }
- }
-
- /**
* Test behavior of isEnhancedOpenSupported
*/
@Test
@@ -1629,7 +1684,7 @@
@Test
public void testGetControllerActivityEnergyInfo() throws Exception {
WifiActivityEnergyInfo activityEnergyInfo =
- new WifiActivityEnergyInfo(5, 3, 3, new long[]{5L, 5L, 5L}, 5, 5, 5, 5);
+ new WifiActivityEnergyInfo(5, 3, 3, 5, 5, 5, 5);
when(mWifiService.reportActivityInfo()).thenReturn(activityEnergyInfo);
assertEquals(activityEnergyInfo, mWifiManager.getControllerActivityEnergyInfo());
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index 6990089..ce085f5 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -64,12 +64,13 @@
assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
suggestion.wifiConfiguration.meteredOverride);
assertEquals(-1, suggestion.wifiConfiguration.priority);
+ assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
}
/**
* Validate correctness of WifiNetworkSuggestion object created by
* {@link WifiNetworkSuggestion.Builder#build()} for WPA_EAP network which requires
- * app interaction and has a priority of zero set.
+ * app interaction, not share credential and has a priority of zero set.
*/
@Test
public void
@@ -78,6 +79,7 @@
.setSsid(TEST_SSID)
.setWpa2Passphrase(TEST_PRESHARED_KEY)
.setIsAppInteractionRequired(true)
+ .setIsUserAllowedToManuallyConnect(false)
.setPriority(0)
.build();
@@ -91,6 +93,7 @@
assertEquals(WifiConfiguration.METERED_OVERRIDE_NONE,
suggestion.wifiConfiguration.meteredOverride);
assertEquals(0, suggestion.wifiConfiguration.priority);
+ assertEquals(false, suggestion.isUserAllowedToManuallyConnect);
}
/**
@@ -118,6 +121,7 @@
assertEquals(WifiConfiguration.METERED_OVERRIDE_METERED,
suggestion.wifiConfiguration.meteredOverride);
assertEquals(-1, suggestion.wifiConfiguration.priority);
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
}
/**
@@ -138,6 +142,7 @@
.get(WifiConfiguration.KeyMgmt.OWE));
assertNull(suggestion.wifiConfiguration.preSharedKey);
assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertFalse(suggestion.isUserAllowedToManuallyConnect);
}
/**
@@ -149,6 +154,7 @@
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
.setWpa3Passphrase(TEST_PRESHARED_KEY)
+ .setIsUserAllowedToManuallyConnect(true)
.build();
assertEquals("\"" + TEST_SSID + "\"", suggestion.wifiConfiguration.SSID);
@@ -157,6 +163,7 @@
assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
suggestion.wifiConfiguration.preSharedKey);
assertTrue(suggestion.wifiConfiguration.requirePMF);
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
}
@@ -186,6 +193,7 @@
assertNull(suggestion.wifiConfiguration.preSharedKey);
// allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
// here.
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
}
/**
@@ -205,6 +213,7 @@
assertTrue(suggestion.isAppInteractionRequired);
assertEquals(suggestion.wifiConfiguration.meteredOverride,
WifiConfiguration.METERED_OVERRIDE_METERED);
+ assertTrue(suggestion.isUserAllowedToManuallyConnect);
}
/**
@@ -439,7 +448,7 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
- configuration, null, false, true, TEST_UID, TEST_PACKAGE_NAME);
+ configuration, null, false, true, true, TEST_UID, TEST_PACKAGE_NAME);
Parcel parcelW = Parcel.obtain();
suggestion.writeToParcel(parcelW, 0);
@@ -506,7 +515,7 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, true, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, true, false, true, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
@@ -514,7 +523,7 @@
configuration1.BSSID = TEST_BSSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, true, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, true, true, TEST_UID,
TEST_PACKAGE_NAME);
assertEquals(suggestion, suggestion1);
@@ -531,14 +540,14 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID_1;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -555,14 +564,14 @@
configuration.BSSID = TEST_BSSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -578,14 +587,14 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
WifiConfiguration configuration1 = new WifiConfiguration();
configuration1.SSID = TEST_SSID;
configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration1, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration1, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -601,11 +610,11 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion =
- new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID,
+ new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID,
TEST_PACKAGE_NAME);
WifiNetworkSuggestion suggestion1 =
- new WifiNetworkSuggestion(configuration, null, false, false, TEST_UID_OTHER,
+ new WifiNetworkSuggestion(configuration, null, false, false, true, TEST_UID_OTHER,
TEST_PACKAGE_NAME);
assertNotEquals(suggestion, suggestion1);
@@ -621,10 +630,10 @@
configuration.SSID = TEST_SSID;
configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
- configuration, null, false, false, TEST_UID, TEST_PACKAGE_NAME);
+ configuration, null, false, false, true, TEST_UID, TEST_PACKAGE_NAME);
WifiNetworkSuggestion suggestion1 = new WifiNetworkSuggestion(
- configuration, null, false, false, TEST_UID, TEST_PACKAGE_NAME_OTHER);
+ configuration, null, false, false, true, TEST_UID, TEST_PACKAGE_NAME_OTHER);
assertNotEquals(suggestion, suggestion1);
}
@@ -664,4 +673,17 @@
.build();
assertNotEquals(suggestion, suggestion1);
}
+
+ /**
+ * Ensure {@link WifiNetworkSuggestion.Builder#build()} throws an exception
+ * when {@link WifiNetworkSuggestion.Builder#setIsUserAllowedToManuallyConnect(boolean)} to
+ * true on a open network suggestion.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testSetIsUserAllowedToManuallyConnectToWithOpenNetwork() {
+ WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
+ .setSsid(TEST_SSID)
+ .setIsUserAllowedToManuallyConnect(true)
+ .build();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index f4fa38b..b1436c90 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -22,7 +22,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.validateMockitoUsage;
@@ -33,6 +35,7 @@
import android.net.wifi.WifiScanner.PnoSettings;
import android.net.wifi.WifiScanner.PnoSettings.PnoNetwork;
import android.net.wifi.WifiScanner.ScanData;
+import android.net.wifi.WifiScanner.ScanListener;
import android.net.wifi.WifiScanner.ScanSettings;
import android.os.Bundle;
import android.os.Handler;
@@ -51,8 +54,10 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
import java.util.Arrays;
+import java.util.concurrent.Executor;
/**
* Unit tests for {@link android.net.wifi.WifiScanner}.
@@ -63,6 +68,13 @@
private Context mContext;
@Mock
private IWifiScanner mService;
+ @Spy
+ private Executor mExecutor = new SynchronousExecutor();
+ @Mock
+ private ScanListener mScanListener;
+ @Mock
+ private WifiScanner.ParcelableScanData mParcelableScanData;
+ private ScanData[] mScanData = {};
private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false;
private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
@@ -76,6 +88,7 @@
private static final String TEST_SSID_2 = "TEST2";
private static final int[] TEST_FREQUENCIES_1 = {};
private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+ private static final String DESCRIPTION_NOT_AUTHORIZED = "Not authorized";
private WifiScanner mWifiScanner;
private TestLooper mLooper;
@@ -95,6 +108,7 @@
when(mService.getMessenger()).thenReturn(mBidirectionalAsyncChannelServer.getMessenger());
mWifiScanner = new WifiScanner(mContext, mService, mLooper.getLooper());
mLooper.dispatchAll();
+ when(mParcelableScanData.getResults()).thenReturn(mScanData);
}
/**
@@ -111,7 +125,7 @@
@Test
public void verifyScanSettingsParcelWithBand() throws Exception {
ScanSettings writeSettings = new ScanSettings();
- writeSettings.type = WifiScanner.TYPE_LOW_POWER;
+ writeSettings.type = WifiScanner.SCAN_TYPE_LOW_POWER;
writeSettings.band = WifiScanner.WIFI_BAND_BOTH_WITH_DFS;
ScanSettings readSettings = parcelWriteRead(writeSettings);
@@ -126,7 +140,7 @@
@Test
public void verifyScanSettingsParcelWithChannels() throws Exception {
ScanSettings writeSettings = new ScanSettings();
- writeSettings.type = WifiScanner.TYPE_HIGH_ACCURACY;
+ writeSettings.type = WifiScanner.SCAN_TYPE_HIGH_ACCURACY;
writeSettings.band = WifiScanner.WIFI_BAND_UNSPECIFIED;
writeSettings.channels = new WifiScanner.ChannelSpec[] {
new WifiScanner.ChannelSpec(5),
@@ -243,13 +257,13 @@
/**
- * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)}
+ * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)}
* @throws Exception
*/
@Test
public void testStartScan() throws Exception {
ScanSettings scanSettings = new ScanSettings();
- WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class);
+ ScanListener scanListener = mock(ScanListener.class);
mWifiScanner.startScan(scanSettings, scanListener);
mLooper.dispatchAll();
@@ -273,13 +287,13 @@
}
/**
- * Test behavior of {@link WifiScanner#stopScan(WifiScanner.ScanListener)}
+ * Test behavior of {@link WifiScanner#stopScan(ScanListener)}
* @throws Exception
*/
@Test
public void testStopScan() throws Exception {
ScanSettings scanSettings = new ScanSettings();
- WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class);
+ ScanListener scanListener = mock(ScanListener.class);
mWifiScanner.startScan(scanSettings, scanListener);
mLooper.dispatchAll();
@@ -302,13 +316,13 @@
}
/**
- * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)}
+ * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)}
* @throws Exception
*/
@Test
public void testStartScanListenerOnSuccess() throws Exception {
ScanSettings scanSettings = new ScanSettings();
- WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class);
+ ScanListener scanListener = mock(ScanListener.class);
mWifiScanner.startScan(scanSettings, scanListener);
mLooper.dispatchAll();
@@ -332,13 +346,13 @@
}
/**
- * Test behavior of {@link WifiScanner#startScan(ScanSettings, WifiScanner.ScanListener)}
+ * Test behavior of {@link WifiScanner#startScan(ScanSettings, ScanListener)}
* @throws Exception
*/
@Test
public void testStartScanListenerOnResults() throws Exception {
ScanSettings scanSettings = new ScanSettings();
- WifiScanner.ScanListener scanListener = mock(WifiScanner.ScanListener.class);
+ ScanListener scanListener = mock(ScanListener.class);
mWifiScanner.startScan(scanSettings, scanListener);
mLooper.dispatchAll();
@@ -425,7 +439,7 @@
}
/**
- * Test behavior of {@link WifiScanner#stopPnoScan(WifiScanner.ScanListener)}
+ * Test behavior of {@link WifiScanner#stopPnoScan(ScanListener)}
* WifiScanner.PnoScanListener)}
* @throws Exception
*/
@@ -480,4 +494,134 @@
assertEquals(scanData.getResults().length, readScanData.getResults().length);
assertEquals(scanData.getResults()[0].SSID, readScanData.getResults()[0].SSID);
}
+
+ /** Tests that upon registration success, {@link ScanListener#onSuccess()} is called. */
+ @Test
+ public void testRegisterScanListenerSuccess() throws Exception {
+ mWifiScanner.registerScanListener(mExecutor, mScanListener);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandler).handleMessage(messageArgumentCaptor.capture());
+ Message sentMessage = messageArgumentCaptor.getValue();
+ assertNotNull(sentMessage);
+
+ assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size());
+ Messenger scannerMessenger =
+ mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next();
+
+ Message responseMessage = Message.obtain();
+ responseMessage.what = WifiScanner.CMD_OP_SUCCEEDED;
+ responseMessage.arg2 = sentMessage.arg2;
+ scannerMessenger.send(responseMessage);
+ mLooper.dispatchAll();
+
+ verify(mExecutor).execute(any());
+ verify(mScanListener).onSuccess();
+ }
+
+ /**
+ * Tests that upon registration failed, {@link ScanListener#onFailure(int, String)} is called.
+ */
+ @Test
+ public void testRegisterScanListenerFailed() throws Exception {
+ mWifiScanner.registerScanListener(mExecutor, mScanListener);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandler).handleMessage(messageArgumentCaptor.capture());
+ Message sentMessage = messageArgumentCaptor.getValue();
+ assertNotNull(sentMessage);
+
+ assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size());
+ Messenger scannerMessenger =
+ mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next();
+
+ {
+ Message responseMessage = Message.obtain();
+ responseMessage.what = WifiScanner.CMD_OP_FAILED;
+ responseMessage.arg2 = sentMessage.arg2;
+ responseMessage.obj = new WifiScanner.OperationResult(
+ WifiScanner.REASON_NOT_AUTHORIZED, DESCRIPTION_NOT_AUTHORIZED);
+ scannerMessenger.send(responseMessage);
+ mLooper.dispatchAll();
+ }
+
+ verify(mExecutor).execute(any());
+ verify(mScanListener).onFailure(
+ WifiScanner.REASON_NOT_AUTHORIZED, DESCRIPTION_NOT_AUTHORIZED);
+
+ // CMD_OP_FAILED should have caused the removal of the listener, verify this
+ {
+ Message responseMessage = Message.obtain();
+ responseMessage.what = WifiScanner.CMD_SCAN_RESULT;
+ responseMessage.arg2 = sentMessage.arg2;
+ responseMessage.obj = mParcelableScanData;
+ scannerMessenger.send(responseMessage);
+ mLooper.dispatchAll();
+ }
+ // execute() called once before, not called again
+ verify(mExecutor, times(1)).execute(any());
+ // onResults() never triggered
+ verify(mScanListener, never()).onResults(any());
+ }
+
+ /**
+ * Tests that when the ScanListener is triggered, {@link ScanListener#onResults(ScanData[])}
+ * is called.
+ */
+ @Test
+ public void testRegisterScanListenerReceiveScanResults() throws Exception {
+ mWifiScanner.registerScanListener(mExecutor, mScanListener);
+ mLooper.dispatchAll();
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandler).handleMessage(messageArgumentCaptor.capture());
+ Message sentMessage = messageArgumentCaptor.getValue();
+ assertNotNull(sentMessage);
+
+ assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size());
+ Messenger scannerMessenger =
+ mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next();
+
+ Message responseMessage = Message.obtain();
+ responseMessage.what = WifiScanner.CMD_SCAN_RESULT;
+ responseMessage.arg2 = sentMessage.arg2;
+ responseMessage.obj = mParcelableScanData;
+ scannerMessenger.send(responseMessage);
+ mLooper.dispatchAll();
+
+ verify(mExecutor).execute(any());
+ verify(mScanListener).onResults(mScanData);
+ }
+
+ /**
+ * Tests that after unregistering a scan listener, {@link ScanListener#onResults(ScanData[])}
+ * is not called.
+ */
+ @Test
+ public void testUnregisterScanListener() throws Exception {
+ mWifiScanner.registerScanListener(mExecutor, mScanListener);
+ mWifiScanner.unregisterScanListener(mScanListener);
+ mLooper.dispatchAll();
+
+ assertEquals(1, mBidirectionalAsyncChannelServer.getClientMessengers().size());
+ Messenger scannerMessenger =
+ mBidirectionalAsyncChannelServer.getClientMessengers().iterator().next();
+
+ ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mHandler, times(2)).handleMessage(messageArgumentCaptor.capture());
+ Message sentMessage = messageArgumentCaptor.getValue();
+ assertNotNull(sentMessage);
+
+ Message responseMessage = Message.obtain();
+ responseMessage.what = WifiScanner.CMD_SCAN_RESULT;
+ responseMessage.obj = mParcelableScanData;
+ responseMessage.arg2 = sentMessage.arg2;
+ scannerMessenger.send(responseMessage);
+ mLooper.dispatchAll();
+
+ verify(mExecutor, never()).execute(any());
+ verify(mScanListener, never()).onResults(mScanData);
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
index 43ee249..2ded849 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/OsuProviderTest.java
@@ -19,7 +19,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import android.graphics.drawable.Icon;
import android.net.Uri;
import android.net.wifi.WifiSsid;
import android.os.Parcel;
@@ -56,7 +55,6 @@
private static final String TEST_NAI = "test.access.com";
private static final List<Integer> TEST_METHOD_LIST =
Arrays.asList(OsuProvider.METHOD_SOAP_XML_SPP);
- private static final Icon TEST_ICON = Icon.createWithData(new byte[10], 0, 10);
/**
* Verify parcel write and read consistency for the given {@link OsuProvider}.
@@ -82,7 +80,7 @@
*/
@Test
public void verifyParcelWithEmptyProviderInfo() throws Exception {
- verifyParcel(new OsuProvider((WifiSsid) null, null, null, null, null, null, null));
+ verifyParcel(new OsuProvider((WifiSsid) null, null, null, null, null, null));
}
/**
@@ -93,7 +91,7 @@
@Test
public void verifyParcelWithFullProviderInfo() throws Exception {
verifyParcel(new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
- TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON));
+ TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST));
}
/**
@@ -102,7 +100,7 @@
*/
@Test
public void verifyCopyConstructorWithNullSource() throws Exception {
- OsuProvider expected = new OsuProvider((WifiSsid) null, null, null, null, null, null, null);
+ OsuProvider expected = new OsuProvider((WifiSsid) null, null, null, null, null, null);
assertEquals(expected, new OsuProvider(null));
}
@@ -114,7 +112,7 @@
@Test
public void verifyCopyConstructorWithValidSource() throws Exception {
OsuProvider source = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
- TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+ TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST);
assertEquals(source, new OsuProvider(source));
}
@@ -126,7 +124,7 @@
@Test
public void verifyGetters() throws Exception {
OsuProvider provider = new OsuProvider(TEST_SSID, TEST_FRIENDLY_NAMES,
- TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST, TEST_ICON);
+ TEST_SERVICE_DESCRIPTION, TEST_SERVER_URI, TEST_NAI, TEST_METHOD_LIST);
assertTrue(TEST_SSID.equals(provider.getOsuSsid()));
assertTrue(TEST_FRIENDLY_NAME.equals(provider.getFriendlyName()));
@@ -135,6 +133,5 @@
assertTrue(TEST_SERVER_URI.equals(provider.getServerUri()));
assertTrue(TEST_NAI.equals(provider.getNetworkAccessIdentifier()));
assertTrue(TEST_METHOD_LIST.equals(provider.getMethodList()));
- assertTrue(TEST_ICON.sameAs(provider.getIcon()));
}
}