Merge "Move BubbleView methods into BadgedImageView; remove BubbleView"
diff --git a/Android.bp b/Android.bp
index a7ce917..9426a9c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -803,10 +803,9 @@
}
filegroup {
- name: "incremental_data_loader_aidl",
+ name: "dataloader_aidl",
srcs: [
- "core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl",
- "core/java/android/service/incremental/IIncrementalDataLoaderService.aidl",
+ "core/java/android/content/pm/IDataLoaderStatusListener.aidl",
],
path: "core/java",
}
@@ -815,7 +814,27 @@
name: "libincremental_aidl",
srcs: [
":incremental_aidl",
- ":incremental_data_loader_aidl",
+ ],
+ imports: [
+ "libdataloader_aidl",
+ ],
+ backend: {
+ java: {
+ sdk_version: "28",
+ },
+ cpp: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ },
+ },
+}
+
+aidl_interface {
+ name: "libdataloader_aidl",
+ srcs: [
+ ":dataloader_aidl",
],
backend: {
java: {
@@ -1675,6 +1694,7 @@
srcs: [
":framework-annotations",
"core/java/android/os/HandlerExecutor.java",
+ "core/java/android/util/BackupUtils.java",
"core/java/android/util/KeyValueListParser.java",
"core/java/android/util/LocalLog.java",
"core/java/android/util/Rational.java",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1e0b58e..55fa5ed 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -8,5 +8,10 @@
}
]
}
+ ],
+ "postsubmit-managedprofile-stress": [
+ {
+ "name": "ManagedProfileLifecycleStressTest"
+ }
]
}
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
index 5ac922c..3dbb5cf 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -24,10 +24,8 @@
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SearchResultProto;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -94,23 +92,25 @@
* 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.
+ * @return A {@link SearchResultProto} containing the matching documents, which may have no
+ * results if no documents match.
*/
@NonNull
- public List<DocumentProto> query(@NonNull String term) {
+ public SearchResultProto query(@NonNull String term) {
String normTerm = normalizeString(term);
Set<Integer> docIds = mIndex.get(normTerm);
if (docIds == null || docIds.isEmpty()) {
- return Collections.emptyList();
+ return SearchResultProto.getDefaultInstance();
}
- List<DocumentProto> matches = new ArrayList<>(docIds.size());
+ SearchResultProto.Builder results = SearchResultProto.newBuilder();
for (int docId : docIds) {
DocumentProto document = mDocStore.get(docId);
if (document != null) {
- matches.add(document);
+ results.addResults(
+ SearchResultProto.ResultProto.newBuilder().setDocument(document));
}
}
- return matches;
+ return results.build();
}
/**
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 8019d4f..4c44334 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -17,13 +17,10 @@
package android.os;
import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
-import java.util.List;
-
/**
* Access to the service that keeps track of device idleness and drives low power mode based on
* that.
@@ -75,21 +72,6 @@
}
/**
- * Add the specified packages to the power save whitelist.
- *
- * @return the number of packages that were successfully added to the whitelist
- */
- @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
- public int addPowerSaveWhitelistApps(@NonNull List<String> packageNames) {
- try {
- return mService.addPowerSaveWhitelistApps(packageNames);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- return 0;
- }
- }
-
- /**
* Return whether a given package is in the power-save whitelist or not.
* @hide
*/
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 7a3ed92..4ffcf8a 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -26,6 +26,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
/**
* Interface to access and modify the power save whitelist.
@@ -78,6 +80,31 @@
}
/**
+ * Add the specified package to the power save whitelist.
+ *
+ * @return true if the package was successfully added to the whitelist
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public boolean addToWhitelist(@NonNull String packageName) {
+ return addToWhitelist(Collections.singletonList(packageName)) == 1;
+ }
+
+ /**
+ * Add the specified packages to the power save whitelist.
+ *
+ * @return the number of packages that were successfully added to the whitelist
+ */
+ @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+ public int addToWhitelist(@NonNull List<String> packageNames) {
+ try {
+ return mService.addPowerSaveWhitelistApps(packageNames);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return 0;
+ }
+ }
+
+ /**
* Add an app to the temporary whitelist for a short amount of time.
*
* @param packageName The package to add to the temp whitelist
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index bcd8be7..2f8b513 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1171,7 +1171,8 @@
private void fetchCarrierPrivilegedAppsLocked() {
TelephonyManager telephonyManager =
mContext.getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
+ mCarrierPrivilegedApps =
+ telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
mHaveCarrierPrivilegedApps = true;
if (DEBUG) {
Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
index b8dcb90..40f3c45 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkext/Android.bp
@@ -15,7 +15,13 @@
apex {
name: "com.android.sdkext",
manifest: "manifest.json",
+ binaries: [ "derive_sdk" ],
java_libs: [ "framework-sdkext" ],
+ prebuilts: [
+ "com.android.sdkext.ldconfig",
+ "cur_sdkinfo",
+ "derive_sdk.rc",
+ ],
key: "com.android.sdkext.key",
certificate: ":com.android.sdkext.certificate",
}
@@ -30,3 +36,35 @@
name: "com.android.sdkext.certificate",
certificate: "com.android.sdkext",
}
+
+prebuilt_etc {
+ name: "com.android.sdkext.ldconfig",
+ src: "ld.config.txt",
+ filename: "ld.config.txt",
+ installable: false,
+}
+
+python_binary_host {
+ name: "gen_sdkinfo",
+ srcs: [
+ "derive_sdk/sdk.proto",
+ "gen_sdkinfo.py",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+}
+
+gensrcs {
+ name: "cur_sdkinfo_src",
+ srcs: [""],
+ tools: [ "gen_sdkinfo" ],
+ cmd: "$(location) -v 0 -o $(out)",
+}
+
+prebuilt_etc {
+ name: "cur_sdkinfo",
+ src: ":cur_sdkinfo_src",
+ filename: "sdkinfo.binarypb",
+ installable: false,
+}
diff --git a/apex/sdkext/derive_sdk/Android.bp b/apex/sdkext/derive_sdk/Android.bp
new file mode 100644
index 0000000..c4e3c29
--- /dev/null
+++ b/apex/sdkext/derive_sdk/Android.bp
@@ -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.
+
+cc_binary {
+ name: "derive_sdk",
+ srcs: [
+ "derive_sdk.cpp",
+ "sdk.proto",
+ ],
+ proto: {
+ type: "lite",
+ },
+ sdk_version: "current",
+ stl: "c++_static",
+ shared_libs: [ "liblog" ],
+ static_libs: [
+ "libbase_ndk",
+ "libprotobuf-cpp-lite-ndk",
+ ],
+}
+
+prebuilt_etc {
+ name: "derive_sdk.rc",
+ src: "derive_sdk.rc",
+ installable: false,
+}
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp
new file mode 100644
index 0000000..0aacebe
--- /dev/null
+++ b/apex/sdkext/derive_sdk/derive_sdk.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "derive_sdk"
+
+#include <algorithm>
+#include <dirent.h>
+#include <iostream>
+#include <sys/stat.h>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "frameworks/base/apex/sdkext/derive_sdk/sdk.pb.h"
+
+using com::android::sdkext::proto::SdkVersion;
+
+int main(int, char**) {
+ std::unique_ptr<DIR, decltype(&closedir)> apex(opendir("/apex"), closedir);
+ if (!apex) {
+ LOG(ERROR) << "Could not read /apex";
+ return EXIT_FAILURE;
+ }
+ struct dirent* de;
+ std::vector<std::string> paths;
+ while ((de = readdir(apex.get()))) {
+ std::string name = de->d_name;
+ if (name[0] == '.' || name.find('@') != std::string::npos) {
+ // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
+ continue;
+ }
+ std::string path = "/apex/" + name + "/etc/sdkinfo.binarypb";
+ struct stat statbuf;
+ if (stat(path.c_str(), &statbuf) == 0) {
+ paths.push_back(path);
+ }
+ }
+
+ std::vector<int> versions;
+ for (const auto& path : paths) {
+ std::string contents;
+ if (!android::base::ReadFileToString(path, &contents, true)) {
+ LOG(ERROR) << "failed to read " << path;
+ continue;
+ }
+ SdkVersion sdk_version;
+ if (!sdk_version.ParseFromString(contents)) {
+ LOG(ERROR) << "failed to parse " << path;
+ continue;
+ }
+ versions.push_back(sdk_version.version());
+ }
+ auto itr = std::min_element(versions.begin(), versions.end());
+ std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
+
+ if (!android::base::SetProperty("persist.com.android.sdkext.sdk_info", prop_value)) {
+ LOG(ERROR) << "failed to set sdk_info prop";
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/apex/sdkext/derive_sdk/derive_sdk.rc b/apex/sdkext/derive_sdk/derive_sdk.rc
new file mode 100644
index 0000000..1b66794
--- /dev/null
+++ b/apex/sdkext/derive_sdk/derive_sdk.rc
@@ -0,0 +1,3 @@
+service derive_sdk /apex/com.android.sdkext/bin/derive_sdk
+ oneshot
+ disabled
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/apex/sdkext/derive_sdk/sdk.proto
similarity index 63%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to apex/sdkext/derive_sdk/sdk.proto
index 007ec94..d15b935 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/apex/sdkext/derive_sdk/sdk.proto
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.net.wifi;
+syntax = "proto3";
+package com.android.sdkext.proto;
-parcelable WifiActivityEnergyInfo;
+option java_outer_classname = "SdkProto";
+option optimize_for = LITE_RUNTIME;
+
+message SdkVersion {
+ int32 version = 1;
+}
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkext/gen_sdkinfo.py
new file mode 100644
index 0000000..5af478b
--- /dev/null
+++ b/apex/sdkext/gen_sdkinfo.py
@@ -0,0 +1,19 @@
+import sdk_pb2
+import sys
+
+if __name__ == '__main__':
+ argv = sys.argv[1:]
+ if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
+ print('usage: gen_sdkinfo -v <version> -o <output-file>')
+ sys.exit(1)
+
+ for i in range(len(argv)):
+ if sys.argv[i] == '-o':
+ filename = sys.argv[i+1]
+ if sys.argv[i] == '-v':
+ version = int(sys.argv[i+1])
+
+ proto = sdk_pb2.SdkVersion()
+ proto.version = version
+ with open(filename, 'wb') as f:
+ f.write(proto.SerializeToString())
diff --git a/apex/sdkext/ld.config.txt b/apex/sdkext/ld.config.txt
new file mode 100644
index 0000000..b447068
--- /dev/null
+++ b/apex/sdkext/ld.config.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Bionic loader config file for the sdkext apex.
+
+dir.sdkext = /apex/com.android.sdkext/bin/
+
+[sdkext]
+additional.namespaces = platform
+
+namespace.default.isolated = true
+namespace.default.links = platform
+namespace.default.link.platform.allow_all_shared_libs = true
+
+###############################################################################
+# "platform" namespace: used for NDK libraries
+###############################################################################
+namespace.platform.isolated = true
+namespace.platform.search.paths = /system/${LIB}
+namespace.platform.asan.search.paths = /data/asan/system/${LIB}
+
+# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
+# Add /apex/... path to the permitted paths because linker uses realpath(3)
+# to check the accessibility of the lib. We could add this to search.paths
+# instead but that makes the resolution of bionic libs be dependent on
+# the order of /system/lib and /apex/... in search.paths. If /apex/...
+# is after /system/lib, then /apex/... is never tried because libc.so
+# is always found in /system/lib but fails to pass the accessibility test
+# because of its realpath. It's better to not depend on the ordering if
+# possible.
+namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
+namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
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 f11d4f8..f10db6c 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -64,8 +64,7 @@
import android.net.Network;
import android.net.NetworkRequest;
import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiManager;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
@@ -98,6 +97,7 @@
import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
@@ -170,6 +170,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -342,7 +343,7 @@
private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
- private IWifiManager mWifiManager = null;
+ private WifiManager mWifiManager = null;
private TelephonyManager mTelephony = null;
@GuardedBy("sStatsdLock")
private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
@@ -1148,35 +1149,48 @@
private void pullWifiActivityInfo(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
+ WifiManager wifiManager;
synchronized (this) {
if (mWifiManager == null) {
- mWifiManager =
- IWifiManager.Stub.asInterface(
- ServiceManager.getService(Context.WIFI_SERVICE));
+ mWifiManager = mContext.getSystemService(WifiManager.class);
}
+ wifiManager = mWifiManager;
}
- if (mWifiManager != null) {
- try {
- SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
- final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
- wallClockNanos);
- e.writeLong(wifiInfo.getTimeSinceBootMillis());
- e.writeInt(wifiInfo.getStackState());
- 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,
- "Pulling wifiManager for wifi controller activity energy info has error",
- e);
- } finally {
- Binder.restoreCallingIdentity(token);
+ if (wifiManager == null) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+ wifiManager.getWifiActivityEnergyInfoAsync(
+ new Executor() {
+ @Override
+ public void execute(Runnable runnable) {
+ // run the listener on the binder thread, if it was run on the main
+ // thread it would deadlock since we would be waiting on ourselves
+ runnable.run();
+ }
+ },
+ info -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ wifiReceiver.send(0, bundle);
+ }
+ );
+ final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+ if (wifiInfo == null) {
+ return;
}
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeLong(wifiInfo.getTimeSinceBootMillis());
+ e.writeInt(wifiInfo.getStackState());
+ e.writeLong(wifiInfo.getControllerTxDurationMillis());
+ e.writeLong(wifiInfo.getControllerRxDurationMillis());
+ e.writeLong(wifiInfo.getControllerIdleDurationMillis());
+ e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
+ pulledData.add(e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
diff --git a/api/current.txt b/api/current.txt
index 91a253e..5bdb691 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,6 +299,7 @@
field public static final int animateFirstView = 16843477; // 0x10102d5
field public static final int animateLayoutChanges = 16843506; // 0x10102f2
field public static final int animateOnClick = 16843356; // 0x101025c
+ field public static final int animatedImageDrawable = 16844298; // 0x101060a
field public static final int animation = 16843213; // 0x10101cd
field public static final int animationCache = 16842989; // 0x10100ed
field public static final int animationDuration = 16843026; // 0x1010112
@@ -723,6 +724,7 @@
field public static final int host = 16842792; // 0x1010028
field public static final int hotSpotX = 16844055; // 0x1010517
field public static final int hotSpotY = 16844056; // 0x1010518
+ field public static final int htmlDescription = 16844299; // 0x101060b
field public static final int hyphenationFrequency = 16843998; // 0x10104de
field public static final int icon = 16842754; // 0x1010002
field @Deprecated public static final int iconPreview = 16843337; // 0x1010249
@@ -2926,6 +2928,7 @@
method public int describeContents();
method public static String feedbackTypeToString(int);
method public static String flagToString(int);
+ method public int getAnimatedImageRes();
method @Deprecated public boolean getCanRetrieveWindowContent();
method public int getCapabilities();
method @Deprecated public String getDescription();
@@ -2935,6 +2938,7 @@
method public android.content.pm.ResolveInfo getResolveInfo();
method public String getSettingsActivityName();
method public String loadDescription(android.content.pm.PackageManager);
+ method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager);
method public CharSequence loadSummary(android.content.pm.PackageManager);
method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -5177,12 +5181,12 @@
method public android.content.Intent getResultData();
}
- public abstract class IntentService extends android.app.Service {
- ctor public IntentService(String);
- method @Nullable public android.os.IBinder onBind(android.content.Intent);
- method @WorkerThread protected abstract void onHandleIntent(@Nullable android.content.Intent);
- method public void onStart(@Nullable android.content.Intent, int);
- method public void setIntentRedelivery(boolean);
+ @Deprecated public abstract class IntentService extends android.app.Service {
+ ctor @Deprecated public IntentService(String);
+ method @Deprecated @Nullable public android.os.IBinder onBind(android.content.Intent);
+ method @Deprecated @WorkerThread protected abstract void onHandleIntent(@Nullable android.content.Intent);
+ method @Deprecated public void onStart(@Nullable android.content.Intent, int);
+ method @Deprecated public void setIntentRedelivery(boolean);
}
public class KeyguardManager {
@@ -11361,6 +11365,15 @@
field public int version;
}
+ public final class InstallSourceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getInitiatingPackageName();
+ method @Nullable public String getInstallingPackageName();
+ method @Nullable public String getOriginatingPackageName();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallSourceInfo> CREATOR;
+ }
+
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public InstrumentationInfo();
ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -11725,10 +11738,11 @@
method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
+ method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
- method @Nullable public abstract String getInstallerPackageName(@NonNull String);
+ method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String);
method @NonNull public abstract byte[] getInstantAppCookie();
method public abstract int getInstantAppCookieMaxBytes();
method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -25060,6 +25074,7 @@
method @NonNull public java.util.List<byte[]> getSecureStopIds();
method @NonNull public java.util.List<byte[]> getSecureStops();
method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
+ method @NonNull public static java.util.List<java.util.UUID> getSupportedCryptoSchemes();
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
@@ -25600,6 +25615,50 @@
field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
}
+ public final class MediaParser {
+ method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException;
+ method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
+ method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
+ method @Nullable public String getExtractorName();
+ method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat);
+ method public void release();
+ method public void seek(@NonNull android.media.MediaParser.SeekPoint);
+ }
+
+ public static interface MediaParser.InputReader {
+ method public long getLength();
+ method public long getPosition();
+ method public int read(@NonNull byte[], int, int) throws java.io.IOException, java.lang.InterruptedException;
+ }
+
+ public static interface MediaParser.OutputConsumer {
+ method public void onFormat(int, @NonNull android.media.MediaFormat);
+ method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+ method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException, java.lang.InterruptedException;
+ method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap);
+ method public void onTracksFound(int);
+ }
+
+ public static interface MediaParser.SeekMap {
+ method public long getDurationUs();
+ method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
+ method public boolean isSeekable();
+ field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
+ }
+
+ public static final class MediaParser.SeekPoint {
+ field @NonNull public static final android.media.MediaParser.SeekPoint START;
+ field public final long position;
+ field public final long timeUs;
+ }
+
+ public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
+ method public void seekToPosition(long);
+ }
+
+ public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException {
+ }
+
public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
ctor public MediaPlayer();
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -29264,12 +29323,14 @@
method public static long getMobileRxPackets();
method public static long getMobileTxBytes();
method public static long getMobileTxPackets();
+ method public static long getRxPackets(@NonNull String);
method public static int getThreadStatsTag();
method public static int getThreadStatsUid();
method public static long getTotalRxBytes();
method public static long getTotalRxPackets();
method public static long getTotalTxBytes();
method public static long getTotalTxPackets();
+ method public static long getTxPackets(@NonNull String);
method public static long getUidRxBytes(int);
method public static long getUidRxPackets(int);
method @Deprecated public static long getUidTcpRxBytes(int);
@@ -36873,6 +36934,7 @@
method @WorkerThread public static boolean isBlocked(android.content.Context, String);
method @WorkerThread public static int unblock(android.content.Context, String);
field public static final String AUTHORITY = "com.android.blockednumber";
+ field public static final android.net.Uri AUTHORITY_URI;
}
public static class BlockedNumberContract.BlockedNumbers {
@@ -38612,15 +38674,13 @@
ctor public MediaStore();
method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
- method public static boolean getIncludePending(@NonNull android.net.Uri);
method public static android.net.Uri getMediaScannerUri();
method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method public static boolean getRequireOriginal(@NonNull android.net.Uri);
method @NonNull public static String getVersion(@NonNull android.content.Context);
method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
- method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
- method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
+ method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
@@ -38656,9 +38716,17 @@
field public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH = "android.media.action.TEXT_OPEN_FROM_SEARCH";
field public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
field public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH = "android.media.action.VIDEO_PLAY_FROM_SEARCH";
+ field public static final int MATCH_DEFAULT = 0; // 0x0
+ field public static final int MATCH_EXCLUDE = 2; // 0x2
+ field public static final int MATCH_INCLUDE = 1; // 0x1
+ field public static final int MATCH_ONLY = 3; // 0x3
field public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
field public static final String MEDIA_SCANNER_VOLUME = "volume";
field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service";
+ field public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
+ field public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
+ field public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
+ field public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
field public static final String UNKNOWN_STRING = "<unknown>";
field public static final String VOLUME_EXTERNAL = "external";
field public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary";
@@ -43481,6 +43549,7 @@
method public int getCallProperties();
method public String getCallerDisplayName();
method public int getCallerDisplayNamePresentation();
+ method public int getCallerNumberVerificationStatus();
method public final long getConnectTimeMillis();
method public long getCreationTimeMillis();
method public android.telecom.DisconnectCause getDisconnectCause();
@@ -43662,6 +43731,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final String getCallerDisplayName();
method public final int getCallerDisplayNamePresentation();
+ method public int getCallerNumberVerificationStatus();
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
@@ -43713,6 +43783,7 @@
method public final void setAudioModeIsVoip(boolean);
method public final void setAudioRoute(int);
method public final void setCallerDisplayName(String, int);
+ method public void setCallerNumberVerificationStatus(int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
@@ -43732,6 +43803,27 @@
method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
method public final void setVideoState(int);
method public static String stateToString(int);
+ field public static final int AUDIO_CODEC_AMR = 1; // 0x1
+ field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
+ field public static final int AUDIO_CODEC_EVRC = 4; // 0x4
+ field public static final int AUDIO_CODEC_EVRC_B = 5; // 0x5
+ field public static final int AUDIO_CODEC_EVRC_NW = 7; // 0x7
+ field public static final int AUDIO_CODEC_EVRC_WB = 6; // 0x6
+ field public static final int AUDIO_CODEC_EVS_FB = 20; // 0x14
+ field public static final int AUDIO_CODEC_EVS_NB = 17; // 0x11
+ field public static final int AUDIO_CODEC_EVS_SWB = 19; // 0x13
+ field public static final int AUDIO_CODEC_EVS_WB = 18; // 0x12
+ field public static final int AUDIO_CODEC_G711A = 13; // 0xd
+ field public static final int AUDIO_CODEC_G711AB = 15; // 0xf
+ field public static final int AUDIO_CODEC_G711U = 11; // 0xb
+ field public static final int AUDIO_CODEC_G722 = 14; // 0xe
+ field public static final int AUDIO_CODEC_G723 = 12; // 0xc
+ field public static final int AUDIO_CODEC_G729 = 16; // 0x10
+ field public static final int AUDIO_CODEC_GSM_EFR = 8; // 0x8
+ field public static final int AUDIO_CODEC_GSM_FR = 9; // 0x9
+ field public static final int AUDIO_CODEC_GSM_HR = 10; // 0xa
+ field public static final int AUDIO_CODEC_NONE = 0; // 0x0
+ field public static final int AUDIO_CODEC_QCELP13K = 3; // 0x3
field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
@@ -43765,6 +43857,7 @@
field public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
+ field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC";
field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
@@ -43786,6 +43879,9 @@
field public static final int STATE_NEW = 1; // 0x1
field public static final int STATE_PULLING_CALL = 7; // 0x7
field public static final int STATE_RINGING = 2; // 0x2
+ field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
+ field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
+ field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
}
public static final class Connection.RttModifyStatus {
@@ -44533,6 +44629,7 @@
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
@@ -45002,6 +45099,12 @@
field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
}
+ public final class PhoneCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
+ }
+
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
ctor public PhoneNumberFormattingTextWatcher();
ctor public PhoneNumberFormattingTextWatcher(String);
@@ -46738,30 +46841,30 @@
public static class Layout.Directions {
}
- public abstract class LoginFilter implements android.text.InputFilter {
- method public CharSequence filter(CharSequence, int, int, android.text.Spanned, int, int);
- method public abstract boolean isAllowed(char);
- method public void onInvalidCharacter(char);
- method public void onStart();
- method public void onStop();
+ @Deprecated public abstract class LoginFilter implements android.text.InputFilter {
+ method @Deprecated public CharSequence filter(CharSequence, int, int, android.text.Spanned, int, int);
+ method @Deprecated public abstract boolean isAllowed(char);
+ method @Deprecated public void onInvalidCharacter(char);
+ method @Deprecated public void onStart();
+ method @Deprecated public void onStop();
}
- public static class LoginFilter.PasswordFilterGMail extends android.text.LoginFilter {
- ctor public LoginFilter.PasswordFilterGMail();
- ctor public LoginFilter.PasswordFilterGMail(boolean);
- method public boolean isAllowed(char);
+ @Deprecated public static class LoginFilter.PasswordFilterGMail extends android.text.LoginFilter {
+ ctor @Deprecated public LoginFilter.PasswordFilterGMail();
+ ctor @Deprecated public LoginFilter.PasswordFilterGMail(boolean);
+ method @Deprecated public boolean isAllowed(char);
}
- public static class LoginFilter.UsernameFilterGMail extends android.text.LoginFilter {
- ctor public LoginFilter.UsernameFilterGMail();
- ctor public LoginFilter.UsernameFilterGMail(boolean);
- method public boolean isAllowed(char);
+ @Deprecated public static class LoginFilter.UsernameFilterGMail extends android.text.LoginFilter {
+ ctor @Deprecated public LoginFilter.UsernameFilterGMail();
+ ctor @Deprecated public LoginFilter.UsernameFilterGMail(boolean);
+ method @Deprecated public boolean isAllowed(char);
}
- public static class LoginFilter.UsernameFilterGeneric extends android.text.LoginFilter {
- ctor public LoginFilter.UsernameFilterGeneric();
- ctor public LoginFilter.UsernameFilterGeneric(boolean);
- method public boolean isAllowed(char);
+ @Deprecated public static class LoginFilter.UsernameFilterGeneric extends android.text.LoginFilter {
+ ctor @Deprecated public LoginFilter.UsernameFilterGeneric();
+ ctor @Deprecated public LoginFilter.UsernameFilterGeneric(boolean);
+ method @Deprecated public boolean isAllowed(char);
}
public interface NoCopySpan {
diff --git a/api/removed.txt b/api/removed.txt
index e0e26f7..1a2f434 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -438,7 +438,9 @@
public final class MediaStore {
method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams);
method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context);
+ method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri);
method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri);
+ method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
}
public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8bb1983..c7bde93 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1198,6 +1198,7 @@
field public static final int ACTION_DISMISS = 2; // 0x2
field public static final int ACTION_LAUNCH = 1; // 0x1
field public static final int ACTION_PIN = 3; // 0x3
+ field public static final int ACTION_UNPIN = 4; // 0x4
field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
}
@@ -1398,6 +1399,7 @@
public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
@@ -1432,6 +1434,37 @@
field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
}
+ public final class BluetoothUuid {
+ method public static boolean containsAnyUuid(@Nullable android.os.ParcelUuid[], @Nullable android.os.ParcelUuid[]);
+ method @NonNull public static android.os.ParcelUuid parseUuidFrom(@Nullable byte[]);
+ field @NonNull public static final android.os.ParcelUuid A2DP_SINK;
+ field @NonNull public static final android.os.ParcelUuid A2DP_SOURCE;
+ field @NonNull public static final android.os.ParcelUuid ADV_AUDIO_DIST;
+ field @NonNull public static final android.os.ParcelUuid AVRCP_CONTROLLER;
+ field @NonNull public static final android.os.ParcelUuid AVRCP_TARGET;
+ field @NonNull public static final android.os.ParcelUuid BASE_UUID;
+ field @NonNull public static final android.os.ParcelUuid BNEP;
+ field @NonNull public static final android.os.ParcelUuid HEARING_AID;
+ field @NonNull public static final android.os.ParcelUuid HFP;
+ field @NonNull public static final android.os.ParcelUuid HFP_AG;
+ field @NonNull public static final android.os.ParcelUuid HID;
+ field @NonNull public static final android.os.ParcelUuid HOGP;
+ field @NonNull public static final android.os.ParcelUuid HSP;
+ field @NonNull public static final android.os.ParcelUuid HSP_AG;
+ field @NonNull public static final android.os.ParcelUuid MAP;
+ field @NonNull public static final android.os.ParcelUuid MAS;
+ field @NonNull public static final android.os.ParcelUuid MNS;
+ field @NonNull public static final android.os.ParcelUuid NAP;
+ field @NonNull public static final android.os.ParcelUuid OBEX_OBJECT_PUSH;
+ field @NonNull public static final android.os.ParcelUuid PANU;
+ field @NonNull public static final android.os.ParcelUuid PBAP_PCE;
+ field @NonNull public static final android.os.ParcelUuid PBAP_PSE;
+ field @NonNull public static final android.os.ParcelUuid SAP;
+ field public static final int UUID_BYTES_128_BIT = 16; // 0x10
+ field public static final int UUID_BYTES_16_BIT = 2; // 0x2
+ field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+ }
+
}
package android.bluetooth.le {
@@ -4487,6 +4520,7 @@
public class Network implements android.os.Parcelable {
ctor public Network(@NonNull android.net.Network);
method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
+ field public final int netId;
}
public final class NetworkCapabilities implements android.os.Parcelable {
@@ -4731,7 +4765,7 @@
method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
}
- public abstract class ChildSessionOptions {
+ public abstract class ChildSessionParams {
}
public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
@@ -4776,11 +4810,11 @@
}
public final class IkeSession implements java.lang.AutoCloseable {
- ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
+ ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
method public void close();
method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback);
method public void kill();
- method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionCallback);
+ method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
}
public interface IkeSessionCallback {
@@ -4798,21 +4832,21 @@
field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2
}
- public final class IkeSessionOptions {
+ public final class IkeSessionParams {
}
- public static final class IkeSessionOptions.Builder {
- ctor public IkeSessionOptions.Builder();
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions build();
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthPsk(@NonNull byte[]);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setServerAddress(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
+ public static final class IkeSessionParams.Builder {
+ ctor public IkeSessionParams.Builder();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
}
public final class IkeTrafficSelector {
@@ -4849,33 +4883,33 @@
field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
}
- public final class TransportModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions {
+ public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
}
- public static final class TransportModeChildSessionOptions.Builder {
- ctor public TransportModeChildSessionOptions.Builder();
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions build();
+ public static final class TransportModeChildSessionParams.Builder {
+ ctor public TransportModeChildSessionParams.Builder();
+ method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+ method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+ method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
+ method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build();
}
- public final class TunnelModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions {
+ public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
}
- public static final class TunnelModeChildSessionOptions.Builder {
- ctor public TunnelModeChildSessionOptions.Builder();
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalSubnetRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions build();
+ public static final class TunnelModeChildSessionParams.Builder {
+ ctor public TunnelModeChildSessionParams.Builder();
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalSubnetRequest(int);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
+ method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
}
}
@@ -5340,32 +5374,6 @@
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();
@@ -5502,6 +5510,7 @@
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 void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
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();
@@ -5605,6 +5614,10 @@
method public default void select(@NonNull android.net.wifi.WifiConfiguration);
}
+ public static interface WifiManager.OnWifiActivityEnergyInfoListener {
+ method public void onWifiActivityEnergyInfo(@Nullable android.os.connectivity.WifiActivityEnergyInfo);
+ }
+
public static interface WifiManager.OnWifiUsabilityStatsListener {
method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
}
@@ -5672,10 +5685,13 @@
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_5_6_GHZ = 11; // 0xb
+ field public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = 15; // 0xf
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
field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
+ field public static final int WIFI_BAND_6_GHZ = 8; // 0x8
field public static final int WIFI_BAND_BOTH = 3; // 0x3
field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0
@@ -6363,6 +6379,8 @@
}
public class PowerWhitelistManager {
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean addToWhitelist(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
@@ -6604,6 +6622,32 @@
field @NonNull public static final android.os.Parcelable.Creator<android.os.connectivity.CellularBatteryStats> 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.os.connectivity.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 WifiBatteryStats implements android.os.Parcelable {
method public int describeContents();
method public long getEnergyConsumedMaMillis();
@@ -6708,6 +6752,8 @@
public final class PermissionManager {
method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
}
@@ -6821,7 +6867,6 @@
package android.provider {
public class BlockedNumberContract {
- field @NonNull public static final android.net.Uri AUTHORITY_URI;
field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
field public static final String RES_BLOCK_STATUS = "block_status";
@@ -7069,6 +7114,8 @@
field public static final String DEVICE_DEMO_MODE = "device_demo_mode";
field public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data";
field public static final String EUICC_PROVISIONED = "euicc_provisioned";
+ field public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
+ field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
@@ -8421,6 +8468,10 @@
field public final double lng;
}
+ public class CellBroadcastIntents {
+ method public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+ }
+
public abstract class CellBroadcastService extends android.app.Service {
ctor public CellBroadcastService();
method @CallSuper public android.os.IBinder onBind(@Nullable android.content.Intent);
@@ -9376,6 +9427,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d35736..3ce8c5e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -578,6 +578,7 @@
field public static final int ACTION_DISMISS = 2; // 0x2
field public static final int ACTION_LAUNCH = 1; // 0x1
field public static final int ACTION_PIN = 3; // 0x3
+ field public static final int ACTION_UNPIN = 4; // 0x4
field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
}
@@ -1795,7 +1796,6 @@
}
public class DeviceIdleManager {
- method @RequiresPermission("android.permission.DEVICE_POWER") public int addPowerSaveWhitelistApps(@NonNull java.util.List<java.lang.String>);
method @NonNull public String[] getSystemPowerWhitelist();
method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
}
@@ -2044,6 +2044,8 @@
}
public class PowerWhitelistManager {
+ method @RequiresPermission("android.permission.DEVICE_POWER") public boolean addToWhitelist(@NonNull String);
+ method @RequiresPermission("android.permission.DEVICE_POWER") public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
field public static final int EVENT_MMS = 2; // 0x2
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 3aa8187..a8c4db3 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -8,35 +8,35 @@
ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO:
- Inconsistent extra value; expected `android.telephony.ims.extra.ADDITIONAL_CALL_INFO`, was `AdditionalCallInfo`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE:
- Inconsistent extra value; expected `android.telephony.ims.extra.CALL_RAT_TYPE`, was `CallRadioTech`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CHILD_NUMBER:
- Inconsistent extra value; expected `android.telephony.ims.extra.CHILD_NUMBER`, was `ChildNum`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNA:
- Inconsistent extra value; expected `android.telephony.ims.extra.CNA`, was `cna`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNAP:
- Inconsistent extra value; expected `android.telephony.ims.extra.CNAP`, was `cnap`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CODEC:
- Inconsistent extra value; expected `android.telephony.ims.extra.CODEC`, was `Codec`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DIALSTRING:
- Inconsistent extra value; expected `android.telephony.ims.extra.DIALSTRING`, was `dialstring`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DISPLAY_TEXT:
- Inconsistent extra value; expected `android.telephony.ims.extra.DISPLAY_TEXT`, was `DisplayText`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_EMERGENCY_CALL:
- Inconsistent extra value; expected `android.telephony.ims.extra.EMERGENCY_CALL`, was `e_call`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_IS_CALL_PULL:
- Inconsistent extra value; expected `android.telephony.ims.extra.IS_CALL_PULL`, was `CallPull`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OI:
- Inconsistent extra value; expected `android.telephony.ims.extra.OI`, was `oi`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OIR:
- Inconsistent extra value; expected `android.telephony.ims.extra.OIR`, was `oir`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_REMOTE_URI:
- Inconsistent extra value; expected `android.telephony.ims.extra.REMOTE_URI`, was `remote_uri`
+
ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_USSD:
- Inconsistent extra value; expected `android.telephony.ims.extra.USSD`, was `ussd`
+
ActionValue: android.telephony.ims.ImsReasonInfo#EXTRA_MSG_SERVICE_NOT_AUTHORIZED:
- Inconsistent extra value; expected `android.telephony.ims.extra.MSG_SERVICE_NOT_AUTHORIZED`, was `Forbidden. Not Authorized for Service`
+
ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_CLEANUP:
ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL:
@@ -100,13 +100,13 @@
ArrayReturn: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]):
ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
- Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]`
+
ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1:
- Method parameter should be Collection<ImsCallForwardInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsCallForwardInfo[]`
+
ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
- Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]`
+
ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
- Method parameter should be Collection<Uri> (or subclass) instead of raw array; was `android.net.Uri[]`
+
ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
@@ -268,7 +268,7 @@
ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms():
ConcreteCollection: android.telephony.ims.ImsConferenceState#mParticipants:
- Field type is concrete collection (`java.util.HashMap`); must be higher-level interface
+
ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
@@ -338,9 +338,9 @@
ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
- Registration methods should have overload that accepts delivery Executor: `setListener`
+
ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
- Registration methods should have overload that accepts delivery Executor: `setListener`
+
ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener):
ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener):
@@ -460,11 +460,13 @@
InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
- Internal field mParticipants must not be exposed
+
KotlinOperator: android.os.WorkSource#get(int):
+KotlinOperator: android.util.SparseArrayMap#get(int, String):
+ Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener:
@@ -474,9 +476,9 @@
ListenerInterface: android.os.IncidentManager.AuthListener:
ListenerInterface: android.telephony.ims.ImsCallSessionListener:
- Listeners should be an interface, or otherwise renamed Callback: ImsCallSessionListener
+
ListenerInterface: android.telephony.ims.ImsUtListener:
- Listeners should be an interface, or otherwise renamed Callback: ImsUtListener
+
ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #4:
@@ -496,17 +498,17 @@
ManagerLookup: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int):
- Managers must always be obtained from Context (`createForSubscriptionId`)
+
ManagerLookup: android.telephony.ims.ProvisioningManager#createForSubscriptionId(int):
- Managers must always be obtained from Context (`createForSubscriptionId`)
+
MethodNameTense: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable():
- Unexpected tense; probably meant `enabled`, was `getCapabilitiesToEnable`
+
MethodNameUnits: android.telephony.ims.ImsCallForwardInfo#getTimeSeconds():
- Returned time values must be in milliseconds, was `getTimeSeconds`
+
MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID:
@@ -1460,7 +1462,7 @@
MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent) parameter #0:
MissingNullability: android.telephony.CallQuality#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
+
MissingNullability: android.telephony.DataSpecificRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0:
MissingNullability: android.telephony.LteVopsSupportInfo#writeToParcel(android.os.Parcel, int) parameter #0:
@@ -1478,9 +1480,9 @@
MissingNullability: android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage(String) parameter #0:
MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent):
- Missing nullability on method `getCarrierPackageNamesForIntent` return
+
MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent) parameter #0:
- Missing nullability on parameter `intent` in method `getCarrierPackageNamesForIntent`
+
MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
@@ -1518,315 +1520,315 @@
MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8:
MissingNullability: android.telephony.ims.ImsCallForwardInfo#getNumber():
- Missing nullability on method `getNumber` return
+
MissingNullability: android.telephony.ims.ImsCallForwardInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #2:
- Missing nullability on parameter `callExtras` in method `ImsCallProfile`
+
MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #3:
- Missing nullability on parameter `mediaProfile` in method `ImsCallProfile`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String):
- Missing nullability on method `getCallExtra` return
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtra`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String):
- Missing nullability on method `getCallExtra` return
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtra`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #1:
- Missing nullability on parameter `defaultValue` in method `getCallExtra`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtraBoolean`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String, boolean) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtraBoolean`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtraInt`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String, int) parameter #0:
- Missing nullability on parameter `name` in method `getCallExtraInt`
+
MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtras():
- Missing nullability on method `getCallExtras` return
+
MissingNullability: android.telephony.ims.ImsCallProfile#getMediaProfile():
- Missing nullability on method `getMediaProfile` return
+
MissingNullability: android.telephony.ims.ImsCallProfile#getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `callProfile` in method `getVideoStateFromImsCallProfile`
+
MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #0:
- Missing nullability on parameter `name` in method `setCallExtra`
+
MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #1:
- Missing nullability on parameter `value` in method `setCallExtra`
+
MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraBoolean(String, boolean) parameter #0:
- Missing nullability on parameter `name` in method `setCallExtraBoolean`
+
MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraInt(String, int) parameter #0:
- Missing nullability on parameter `name` in method `setCallExtraInt`
+
MissingNullability: android.telephony.ims.ImsCallProfile#updateCallExtras(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `updateCallExtras`
+
MissingNullability: android.telephony.ims.ImsCallProfile#updateCallType(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `updateCallType`
+
MissingNullability: android.telephony.ims.ImsCallProfile#updateMediaProfile(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `updateMediaProfile`
+
MissingNullability: android.telephony.ims.ImsCallProfile#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionConferenceExtendFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `newSession` in method `callSessionConferenceExtendReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
- Missing nullability on parameter `profile` in method `callSessionConferenceExtendReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `newSession` in method `callSessionConferenceExtended`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
- Missing nullability on parameter `profile` in method `callSessionConferenceExtended`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState) parameter #0:
- Missing nullability on parameter `state` in method `callSessionConferenceStateUpdated`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo) parameter #2:
- Missing nullability on parameter `reasonInfo` in method `callSessionHandover`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo) parameter #2:
- Missing nullability on parameter `reasonInfo` in method `callSessionHandoverFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHeld(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionHeld`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionHoldFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldReceived(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionHoldReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiated(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionInitiated`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionInitiatedFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionInviteParticipantsRequestFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase) parameter #0:
- Missing nullability on parameter `newSession` in method `callSessionMergeComplete`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionMergeFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `newSession` in method `callSessionMergeStarted`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
- Missing nullability on parameter `profile` in method `callSessionMergeStarted`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionProgressing`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionRemoveParticipantsRequestFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionResumeFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeReceived(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionResumeReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumed(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionResumed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttMessageReceived(String) parameter #0:
- Missing nullability on parameter `rttMessage` in method `callSessionRttMessageReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `callProfile` in method `callSessionRttModifyRequestReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification) parameter #0:
- Missing nullability on parameter `suppSrvNotification` in method `callSessionSuppServiceReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionTerminated(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionTerminated`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `reasonInfo` in method `callSessionUpdateFailed`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateReceived(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionUpdateReceived`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdated(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `profile` in method `callSessionUpdated`
+
MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUssdMessageReceived(int, String) parameter #1:
- Missing nullability on parameter `ussdMessage` in method `callSessionUssdMessageReceived`
+
MissingNullability: android.telephony.ims.ImsConferenceState#getConnectionStateForStatus(String) parameter #0:
- Missing nullability on parameter `status` in method `getConnectionStateForStatus`
+
MissingNullability: android.telephony.ims.ImsConferenceState#mParticipants:
- Missing nullability on field `mParticipants` in class `class android.telephony.ims.ImsConferenceState`
+
MissingNullability: android.telephony.ims.ImsConferenceState#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsExternalCallState#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsReasonInfo#ImsReasonInfo(int, int, String) parameter #2:
- Missing nullability on parameter `extraMessage` in method `ImsReasonInfo`
+
MissingNullability: android.telephony.ims.ImsReasonInfo#getExtraMessage():
- Missing nullability on method `getExtraMessage` return
+
MissingNullability: android.telephony.ims.ImsReasonInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsService#createMmTelFeature(int):
- Missing nullability on method `createMmTelFeature` return
+
MissingNullability: android.telephony.ims.ImsService#createRcsFeature(int):
- Missing nullability on method `createRcsFeature` return
+
MissingNullability: android.telephony.ims.ImsService#getConfig(int):
- Missing nullability on method `getConfig` return
+
MissingNullability: android.telephony.ims.ImsService#getRegistration(int):
- Missing nullability on method `getRegistration` return
+
MissingNullability: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) parameter #0:
- Missing nullability on parameter `c` in method `onUpdateSupportedImsFeatures`
+
MissingNullability: android.telephony.ims.ImsService#querySupportedImsFeatures():
- Missing nullability on method `querySupportedImsFeatures` return
+
MissingNullability: android.telephony.ims.ImsSsData#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsSsInfo#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsStreamMediaProfile#copyFrom(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
- Missing nullability on parameter `profile` in method `copyFrom`
+
MissingNullability: android.telephony.ims.ImsStreamMediaProfile#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #4:
- Missing nullability on parameter `number` in method `ImsSuppServiceNotification`
+
MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #5:
- Missing nullability on parameter `history` in method `ImsSuppServiceNotification`
+
MissingNullability: android.telephony.ims.ImsSuppServiceNotification#history:
- Missing nullability on field `history` in class `class android.telephony.ims.ImsSuppServiceNotification`
+
MissingNullability: android.telephony.ims.ImsSuppServiceNotification#number:
- Missing nullability on field `number` in class `class android.telephony.ims.ImsSuppServiceNotification`
+
MissingNullability: android.telephony.ims.ImsSuppServiceNotification#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `out` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.ImsUtListener#onSupplementaryServiceIndication(android.telephony.ims.ImsSsData) parameter #0:
- Missing nullability on parameter `ssData` in method `onSupplementaryServiceIndication`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
- Missing nullability on parameter `cbInfo` in method `onUtConfigurationCallBarringQueried`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1:
- Missing nullability on parameter `cfInfo` in method `onUtConfigurationCallForwardQueried`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
- Missing nullability on parameter `cwInfo` in method `onUtConfigurationCallWaitingQueried`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueried(int, android.os.Bundle) parameter #1:
- Missing nullability on parameter `configuration` in method `onUtConfigurationQueried`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
- Missing nullability on parameter `error` in method `onUtConfigurationQueryFailed`
+
MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
- Missing nullability on parameter `error` in method `onUtConfigurationUpdateFailed`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities) parameter #0:
- Missing nullability on parameter `CameraCapabilities` in method `changeCameraCapabilities`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #0:
- Missing nullability on parameter `fromProfile` in method `onSendSessionModifyRequest`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1:
- Missing nullability on parameter `toProfile` in method `onSendSessionModifyRequest`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyResponse(android.telecom.VideoProfile) parameter #0:
- Missing nullability on parameter `responseProfile` in method `onSendSessionModifyResponse`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String) parameter #0:
- Missing nullability on parameter `cameraId` in method `onSetCamera`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String, int) parameter #0:
- Missing nullability on parameter `cameraId` in method `onSetCamera`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetDisplaySurface(android.view.Surface) parameter #0:
- Missing nullability on parameter `surface` in method `onSetDisplaySurface`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPauseImage(android.net.Uri) parameter #0:
- Missing nullability on parameter `uri` in method `onSetPauseImage`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPreviewSurface(android.view.Surface) parameter #0:
- Missing nullability on parameter `surface` in method `onSetPreviewSurface`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyRequest(android.telecom.VideoProfile) parameter #0:
- Missing nullability on parameter `VideoProfile` in method `receiveSessionModifyRequest`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1:
- Missing nullability on parameter `requestedProfile` in method `receiveSessionModifyResponse`
+
MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #2:
- Missing nullability on parameter `responseProfile` in method `receiveSessionModifyResponse`
+
MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToDisable():
- Missing nullability on method `getCapabilitiesToDisable` return
+
MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable():
- Missing nullability on method `getCapabilitiesToEnable` return
+
MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #0:
- Missing nullability on parameter `request` in method `changeEnabledCapabilities`
+
MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #1:
- Missing nullability on parameter `c` in method `changeEnabledCapabilities`
+
MissingNullability: android.telephony.ims.feature.MmTelFeature#queryCapabilityStatus():
- Missing nullability on method `queryCapabilityStatus` return
+
MissingNullability: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities) parameter #0:
- Missing nullability on parameter `c` in method `MmTelCapabilities`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#accept(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1:
- Missing nullability on parameter `profile` in method `accept`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#deflect(String) parameter #0:
- Missing nullability on parameter `deflectNumber` in method `deflect`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#extendToConference(String[]) parameter #0:
- Missing nullability on parameter `participants` in method `extendToConference`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallId():
- Missing nullability on method `getCallId` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallProfile():
- Missing nullability on method `getCallProfile` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getImsVideoCallProvider():
- Missing nullability on method `getImsVideoCallProvider` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getLocalCallProfile():
- Missing nullability on method `getLocalCallProfile` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String):
- Missing nullability on method `getProperty` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String) parameter #0:
- Missing nullability on parameter `name` in method `getProperty`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getRemoteCallProfile():
- Missing nullability on method `getRemoteCallProfile` return
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#hold(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
- Missing nullability on parameter `profile` in method `hold`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#inviteParticipants(String[]) parameter #0:
- Missing nullability on parameter `participants` in method `inviteParticipants`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#removeParticipants(String[]) parameter #0:
- Missing nullability on parameter `participants` in method `removeParticipants`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#resume(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
- Missing nullability on parameter `profile` in method `resume`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, android.os.Message) parameter #1:
- Missing nullability on parameter `result` in method `sendDtmf`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttMessage(String) parameter #0:
- Missing nullability on parameter `rttMessage` in method `sendRttMessage`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttModifyRequest(android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `toProfile` in method `sendRttModifyRequest`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendUssd(String) parameter #0:
- Missing nullability on parameter `ussdMessage` in method `sendUssd`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener) parameter #0:
- Missing nullability on parameter `listener` in method `setListener`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `callee` in method `start`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #1:
- Missing nullability on parameter `profile` in method `start`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #0:
- Missing nullability on parameter `participants` in method `startConference`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #1:
- Missing nullability on parameter `profile` in method `startConference`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#update(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1:
- Missing nullability on parameter `profile` in method `update`
+
MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase.State#toString(int):
- Missing nullability on method `toString` return
+
MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#getConfigString(int):
- Missing nullability on method `getConfigString` return
+
MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#notifyProvisionedValueChanged(int, String) parameter #1:
- Missing nullability on parameter `value` in method `notifyProvisionedValueChanged`
+
MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#setConfig(int, String) parameter #1:
- Missing nullability on parameter `value` in method `setConfig`
+
MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#getServiceFeatures():
- Missing nullability on method `getServiceFeatures` return
+
MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#writeToParcel(android.os.Parcel, int) parameter #0:
- Missing nullability on parameter `dest` in method `writeToParcel`
+
MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
- Missing nullability on method `addFeature` return
+
MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#build():
- Missing nullability on method `build` return
+
MissingNullability: android.telephony.ims.stub.ImsMultiEndpointImplBase#onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>) parameter #0:
- Missing nullability on parameter `externalCallDialogs` in method `onImsExternalCallStateUpdate`
+
MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered(android.telephony.ims.ImsReasonInfo) parameter #0:
- Missing nullability on parameter `info` in method `onDeregistered`
+
MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
- Missing nullability on parameter `uris` in method `onSubscriberAssociatedUriChanged`
+
MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
- Missing nullability on parameter `info` in method `onTechnologyChangeFailed`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#getSmsFormat():
- Missing nullability on method `getSmsFormat` return
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #1:
- Missing nullability on parameter `format` in method `onSmsReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #2:
- Missing nullability on parameter `pdu` in method `onSmsReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1:
- Missing nullability on parameter `format` in method `onSmsStatusReportReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2:
- Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #2:
- Missing nullability on parameter `format` in method `onSmsStatusReportReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #3:
- Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #2:
- Missing nullability on parameter `format` in method `sendSms`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #3:
- Missing nullability on parameter `smsc` in method `sendSms`
+
MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #5:
- Missing nullability on parameter `pdu` in method `sendSms`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#queryCallForward(int, String) parameter #1:
- Missing nullability on parameter `number` in method `queryCallForward`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener) parameter #0:
- Missing nullability on parameter `listener` in method `setListener`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#transact(android.os.Bundle) parameter #0:
- Missing nullability on parameter `ssInfo` in method `transact`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarring(int, int, String[]) parameter #2:
- Missing nullability on parameter `barrList` in method `updateCallBarring`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarringForServiceClass(int, int, String[], int) parameter #2:
- Missing nullability on parameter `barrList` in method `updateCallBarringForServiceClass`
+
MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallForward(int, int, String, int, int) parameter #2:
- Missing nullability on parameter `number` in method `updateCallForward`
+
MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
@@ -2278,7 +2280,7 @@
NotCloseable: android.os.HwParcel:
NotCloseable: android.telephony.ims.stub.ImsUtImplBase:
- Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.telephony.ims.stub.ImsUtImplBase
+
OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]):
@@ -2292,21 +2294,21 @@
OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#disableIms(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#enableIms(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#getConfig(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#getRegistration(int):
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#querySupportedImsFeatures():
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.ims.ImsService#readyForFeatureCreation():
- If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+
OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int):
OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int):
@@ -2446,7 +2448,7 @@
RethrowRemoteException: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int):
RethrowRemoteException: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration):
- Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+
RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener):
RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener):
@@ -2526,7 +2528,7 @@
SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>):
SamShouldBeLast: android.telephony.ims.ImsMmTelManager#getFeatureState(java.util.function.Consumer<java.lang.Integer>, java.util.concurrent.Executor):
- SAM-compatible parameters (such as parameter 1, "callback", in android.telephony.ims.ImsMmTelManager.getFeatureState) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+
SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
@@ -2599,6 +2601,8 @@
UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
+UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+ When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 22e1d01..bdb8380 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -175,7 +175,11 @@
} else if (opt.equals("--no-hidden-api-checks")) {
instrument.disableHiddenApiChecks = true;
} else if (opt.equals("--no-test-api-checks")) {
- instrument.disableTestApiChecks = true;
+ // TODO(satayev): remove this option, only kept for backwards compatibility with
+ // cached tradefed instance
+ instrument.disableTestApiChecks = false;
+ } else if (opt.equals("--no-test-api-access")) {
+ instrument.disableTestApiChecks = false;
} else if (opt.equals("--no-isolated-storage")) {
instrument.disableIsolatedStorage = true;
} else if (opt.equals("--user")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 6afd7c4..2adbc1f 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -86,7 +86,7 @@
String logPath = null;
public boolean noWindowAnimation = false;
public boolean disableHiddenApiChecks = false;
- public boolean disableTestApiChecks = false;
+ public boolean disableTestApiChecks = true;
public boolean disableIsolatedStorage = false;
public String abi = null;
public int userId = UserHandle.USER_CURRENT;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1fd3abf..e8c0e15 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1275,7 +1275,7 @@
// Permission check not necessary as it's meant for applications to write to
// statsd.
android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
- IPCThreadState::self()->getCallingUid(), label,
+ (int32_t) IPCThreadState::self()->getCallingUid(), label,
state);
return Status::ok();
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b9051da..2efb789 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -50,6 +50,7 @@
import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
import "frameworks/base/core/proto/android/stats/location/location_enums.proto";
import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
+import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -342,6 +343,16 @@
VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
GpsLocationStatusReported gps_location_status_reported = 231;
GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232;
+ MediaProviderScanEvent media_provider_scan_event =
+ 233 [(log_from_module) = "mediaprovider"];
+ MediaProviderDeletionEvent media_provider_deletion_event =
+ 234 [(log_from_module) = "mediaprovider"];
+ MediaProviderPermissionEvent media_provider_permission_event =
+ 235 [(log_from_module) = "mediaprovider"];
+ MediaProviderSchemaChange media_provider_schema_change =
+ 236 [(log_from_module) = "mediaprovider"];
+ MediaProviderIdleMaintenance media_provider_idle_maintenance =
+ 237 [(log_from_module) = "mediaprovider"];
}
// Pulled events will start at field 10000.
@@ -3799,6 +3810,124 @@
optional State state = 2;
}
+/**
+ * Logs when MediaProvider has successfully finished scanning a storage volume.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java
+ */
+message MediaProviderScanEvent {
+ enum Reason {
+ // Scan triggered due to unknown reason
+ UNKNOWN = 0;
+ // Scan triggered due to storage volume being mounted
+ MOUNTED = 1;
+ // Scan triggered due to explicit user action or app request
+ DEMAND = 2;
+ // Scan triggered due to idle maintenance
+ IDLE = 3;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Reason why this scan was triggered
+ optional Reason reason = 2;
+ // Total number of files scanned
+ optional int64 item_count = 3;
+ // Duration of scan, normalized per file
+ optional float normalized_duration_millis = 4;
+ // Number of database inserts, normalized per file
+ optional float normalized_insert_count = 5;
+ // Number of database updates, normalized per file
+ optional float normalized_update_count = 6;
+ // Number of database deletes, normalized per file
+ optional float normalized_delete_count = 7;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to delete media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderDeletionEvent {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Device timestamp when this deletion event occurred
+ optional int64 timestamp_millis = 2;
+ // App that requested deletion
+ optional string package_name = 3;
+ // Number of items that were deleted
+ optional int32 item_count = 4;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to grant them access to media belonging to the user.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java
+ */
+message MediaProviderPermissionEvent {
+ enum Result {
+ UNKNOWN = 0;
+ USER_GRANTED = 1;
+ AUTO_GRANTED = 2;
+ USER_DENIED = 3;
+ USER_DENIED_WITH_PREJUDICE = 4;
+ AUTO_DENIED = 5;
+ }
+
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Device timestamp when this permission event occurred
+ optional int64 timestamp_millis = 2;
+ // App that requested permission
+ optional string package_name = 3;
+ // Number of items that were requested
+ optional int32 item_count = 4;
+ // Result of this request
+ optional Result result = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished upgrading or downgrading its database schema.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java
+ */
+message MediaProviderSchemaChange {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+ // Old database version code
+ optional int32 version_from = 2;
+ // New database version code
+ optional int32 version_to = 3;
+ // Total number of files in database
+ optional int64 item_count = 4;
+ // Duration of schema change, normalized per file
+ optional float normalized_duration_millis = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished an idle maintenance job.
+ *
+ * Logged from:
+ * packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderIdleMaintenance {
+ // Volume type that this event pertains to
+ optional android.stats.mediaprovider.VolumeType volume_type = 1;
+
+ // Total number of files in database
+ optional int64 item_count = 2;
+ // Duration of idle maintenance, normalized per file
+ optional float normalized_duration_millis = 3;
+ // Number of thumbnails found to be stale, normalized per file
+ optional float normalized_stale_thumbnails = 4;
+ // Number of items found to be expired, normalized per file
+ optional float normalized_expired_media = 5;
+}
+
//////////////////////////////////////////////////////////////////////
// Pulled atoms below this line //
//////////////////////////////////////////////////////////////////////
@@ -5798,10 +5927,10 @@
optional int64 request_id = 1;
// UID of package requesting the permission grant
- optional int32 requesting_uid = 2 [(is_uid) = true];
+ optional int32 uid = 2 [(is_uid) = true];
// Name of package requesting the permission grant
- optional string requesting_package_name = 3;
+ optional string package_name = 3;
// The permission to be granted
optional string permission_name = 4;
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a4b3058..1987440 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -16,10 +16,12 @@
package com.android.commands.telecom;
+import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.os.IUserManager;
+import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -33,7 +35,6 @@
import com.android.internal.os.BaseCommand;
import com.android.internal.telecom.ITelecomService;
-import com.android.internal.telephony.ITelephony;
import java.io.PrintStream;
@@ -58,7 +59,6 @@
private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
"add-or-remove-call-companion-app";
- private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
"set-phone-acct-suggestion-component";
private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
@@ -83,7 +83,7 @@
private ComponentName mComponent;
private String mAccountId;
private ITelecomService mTelecomService;
- private ITelephony mTelephonyService;
+ private TelephonyManager mTelephonyManager;
private IUserManager mUserManager;
@Override
@@ -99,7 +99,6 @@
+ "<USER_SN>\n"
+ "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
+ "usage: telecom set-test-call-screening-app <PACKAGE>\n"
- + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
+ "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
+ "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
+ "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
@@ -155,9 +154,10 @@
return;
}
- mTelephonyService = ITelephony.Stub.asInterface(
- ServiceManager.getService(Context.TELEPHONY_SERVICE));
- if (mTelephonyService == null) {
+ Looper.prepareMainLooper();
+ Context context = ActivityThread.systemMain().getSystemContext();
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ if (mTelephonyManager == null) {
Log.w(this, "onRun: Can't access telephony service.");
showError("Error: Could not access the Telephony Service. Is the system running?");
return;
@@ -191,9 +191,6 @@
case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
runAddOrRemoveCallCompanionApp();
break;
- case COMMAND_SET_TEST_AUTO_MODE_APP:
- runSetTestAutoModeApp();
- break;
case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
runSetTestPhoneAcctSuggestionComponent();
break;
@@ -305,11 +302,6 @@
mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
}
- private void runSetTestAutoModeApp() throws RemoteException {
- final String packageName = nextArg();
- mTelecomService.setTestAutoModeApp(packageName);
- }
-
private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
final String componentName = nextArg();
mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
@@ -361,7 +353,7 @@
}
int numSims = Integer.parseInt(nextArgRequired());
System.out.println("Setting sim count to " + numSims + ". Device may reboot");
- mTelephonyService.switchMultiSimConfig(numSims);
+ mTelephonyManager.switchMultiSimConfig(numSims);
}
/**
@@ -375,8 +367,7 @@
private void runGetMaxPhones() throws RemoteException {
// This assumes the max number of SIMs is 2, which it currently is
- if (TelephonyManager.MULTISIM_ALLOWED
- == mTelephonyService.isMultiSimSupported("com.android.commands.telecom", null)) {
+ if (TelephonyManager.MULTISIM_ALLOWED == mTelephonyManager.isMultiSimSupported()) {
System.out.println("2");
} else {
System.out.println("1");
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3db0b26..7fd01db 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -20,6 +20,8 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -512,6 +514,21 @@
private static final long REQUEST_ACCESSIBILITY_BUTTON_CHANGE = 136293963L;
/**
+ * Resource id of the animated image of the accessibility service.
+ */
+ private int mAnimatedImageRes;
+
+ /**
+ * Resource id of the html description of the accessibility service.
+ */
+ private int mHtmlDescriptionRes;
+
+ /**
+ * Non localized html description of the accessibility service.
+ */
+ private String mNonLocalizedHtmlDescription;
+
+ /**
* Creates a new instance.
*/
public AccessibilityServiceInfo() {
@@ -626,6 +643,20 @@
mNonLocalizedSummary = nonLocalizedSummary.toString().trim();
}
}
+ peekedValue = asAttributes.peekValue(
+ com.android.internal.R.styleable.AccessibilityService_animatedImageDrawable);
+ if (peekedValue != null) {
+ mAnimatedImageRes = peekedValue.resourceId;
+ }
+ peekedValue = asAttributes.peekValue(
+ com.android.internal.R.styleable.AccessibilityService_htmlDescription);
+ if (peekedValue != null) {
+ mHtmlDescriptionRes = peekedValue.resourceId;
+ final CharSequence nonLocalizedHtmlDescription = peekedValue.coerceToString();
+ if (nonLocalizedHtmlDescription != null) {
+ mNonLocalizedHtmlDescription = nonLocalizedHtmlDescription.toString().trim();
+ }
+ }
asAttributes.recycle();
} catch (NameNotFoundException e) {
throw new XmlPullParserException( "Unable to create context for: "
@@ -727,6 +758,18 @@
}
/**
+ * The animated image resource id.
+ * <p>
+ * <strong>Statically set from
+ * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ * @return The animated image resource id.
+ */
+ public int getAnimatedImageRes() {
+ return mAnimatedImageRes;
+ }
+
+ /**
* Whether this service can retrieve the current window's content.
* <p>
* <strong>Statically set from
@@ -833,6 +876,29 @@
}
/**
+ * The localized html description of the accessibility service.
+ * <p>
+ * <strong>Statically set from
+ * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+ * </p>
+ * @return The localized html description.
+ */
+ @Nullable
+ public String loadHtmlDescription(@NonNull PackageManager packageManager) {
+ if (mHtmlDescriptionRes == 0) {
+ return mNonLocalizedHtmlDescription;
+ }
+
+ final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
+ final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
+ mHtmlDescriptionRes, serviceInfo.applicationInfo);
+ if (htmlDescription != null) {
+ return htmlDescription.toString().trim();
+ }
+ return null;
+ }
+
+ /**
* Set the recommended time that non-interactive controls need to remain on the screen to
* support the user.
* <p>
@@ -915,7 +981,10 @@
parcel.writeInt(mSummaryResId);
parcel.writeString(mNonLocalizedSummary);
parcel.writeInt(mDescriptionResId);
+ parcel.writeInt(mAnimatedImageRes);
+ parcel.writeInt(mHtmlDescriptionRes);
parcel.writeString(mNonLocalizedDescription);
+ parcel.writeString(mNonLocalizedHtmlDescription);
}
private void initFromParcel(Parcel parcel) {
@@ -934,7 +1003,10 @@
mSummaryResId = parcel.readInt();
mNonLocalizedSummary = parcel.readString();
mDescriptionResId = parcel.readInt();
+ mAnimatedImageRes = parcel.readInt();
+ mHtmlDescriptionRes = parcel.readInt();
mNonLocalizedDescription = parcel.readString();
+ mNonLocalizedHtmlDescription = parcel.readString();
}
@Override
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index 204d71d..1af48cb 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -148,6 +148,18 @@
String publicAlternatives() default "";
/**
+ * Override the default source position when generating an index of the annotations.
+ *
+ * <p>This is intended for use by tools that generate java source code, to point to the
+ * original source position of the annotation, rather than the position within the generated
+ * code. It should never be set manually.
+ *
+ * <p>The format of the value is "path/to/file:startline:startcol:endline:endcol" indicating
+ * the position of the annotation itself.
+ */
+ String overrideSourcePosition() default "";
+
+ /**
* Container for {@link UnsupportedAppUsage} that allows it to be applied repeatedly to types.
*/
@Retention(CLASS)
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0113f69..034826a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,6 +39,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
@@ -2085,6 +2086,21 @@
}
@Override
+ @NonNull
+ public InstallSourceInfo getInstallSourceInfo(String packageName) throws NameNotFoundException {
+ final InstallSourceInfo installSourceInfo;
+ try {
+ installSourceInfo = mPM.getInstallSourceInfo(packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (installSourceInfo == null) {
+ throw new NameNotFoundException(packageName);
+ }
+ return installSourceInfo;
+ }
+
+ @Override
public int getMoveStatus(int moveId) {
try {
return mPM.getMoveStatus(moveId);
@@ -2782,7 +2798,7 @@
public Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo,
@Nullable ApplicationInfo appInfo) {
if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
- // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a
+ // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a
// generic user icon (users generally lack permission to view each other's actual icons)
int targetUserId = itemInfo.showUserIcon;
return UserIcons.getDefaultUserIcon(
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 50d0dab..aa8a302 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -387,6 +387,7 @@
void requestInteractiveBugReport();
void requestFullBugReport();
void requestRemoteBugReport();
+ boolean launchBugReportHandlerApp();
@UnsupportedAppUsage
Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 11c747f..74fb99a 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -44,13 +44,6 @@
* long as necessary (and will not block the application's main loop), but
* only one request will be processed at a time.
*
- * <p class="note"><b>Note:</b> IntentService is subject to all the
- * <a href="/preview/features/background.html">background execution limits</a>
- * imposed with Android 8.0 (API level 26). In most cases, you are better off
- * using {@link android.support.v4.app.JobIntentService}, which uses jobs
- * instead of services when running on Android 8.0 or higher.
- * </p>
- *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For a detailed discussion about how to create services, read the
@@ -59,8 +52,14 @@
* </div>
*
* @see android.support.v4.app.JobIntentService
- * @see android.os.AsyncTask
+ *
+ * @deprecated IntentService is subject to all the
+ * <a href="/preview/features/background.html">background execution limits</a>
+ * imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager}
+ * or {@link androidx.core.app.JobIntentService}, which uses jobs
+ * instead of services when running on Android 8.0 or higher.
*/
+@Deprecated
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
@UnsupportedAppUsage
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 1f91b3f..9b62e3b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -57,7 +57,7 @@
* networking) operations, it should spawn its own thread in which to do that
* work. More information on this can be found in
* <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
- * Threads</a>. The {@link IntentService} class is available
+ * Threads</a>. The {@link androidx.core.app.JobIntentService} class is available
* as a standard implementation of Service that has its own thread where it
* schedules its work to be done.</p>
*
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 11d6528a..2ef0510 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -51,12 +51,13 @@
private static final String TAG = "StatsManager";
private static final boolean DEBUG = false;
+ private static final Object sLock = new Object();
private final Context mContext;
- @GuardedBy("this")
+ @GuardedBy("sLock")
private IStatsManager mService;
- @GuardedBy("this")
+ @GuardedBy("sLock")
private IStatsCompanionService mStatsCompanion;
/**
@@ -125,7 +126,7 @@
*/
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
// can throw IllegalArgumentException
@@ -162,7 +163,7 @@
*/
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public void removeConfig(long configKey) throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
service.removeConfiguration(configKey, mContext.getOpPackageName());
@@ -223,7 +224,7 @@
public void setBroadcastSubscriber(
PendingIntent pendingIntent, long configKey, long subscriberId)
throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (pendingIntent != null) {
@@ -277,7 +278,7 @@
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (pendingIntent == null) {
@@ -315,7 +316,7 @@
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public @NonNull long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent)
throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (pendingIntent == null) {
@@ -363,7 +364,7 @@
*/
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public byte[] getReports(long configKey) throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
return service.getData(configKey, mContext.getOpPackageName());
@@ -400,7 +401,7 @@
*/
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public byte[] getStatsMetadata() throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
return service.getMetadata(mContext.getOpPackageName());
@@ -435,7 +436,7 @@
@RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
public long[] getRegisteredExperimentIds()
throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
@@ -472,7 +473,7 @@
@RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
public void setPullerCallback(int atomTag, IStatsPullerCallback callback)
throws StatsUnavailableException {
- synchronized (this) {
+ synchronized (sLock) {
try {
IStatsManager service = getIStatsManagerLocked();
if (callback == null) {
@@ -515,7 +516,7 @@
if (additiveFields == null) {
additiveFields = new int[0];
}
- synchronized (this) {
+ synchronized (sLock) {
IStatsCompanionService service = getIStatsCompanionServiceLocked();
PullAtomCallbackInternal rec =
new PullAtomCallbackInternal(atomTag, callback, executor);
@@ -649,13 +650,13 @@
private class StatsdDeathRecipient implements IBinder.DeathRecipient {
@Override
public void binderDied() {
- synchronized (this) {
+ synchronized (sLock) {
mService = null;
}
}
}
- @GuardedBy("this")
+ @GuardedBy("sLock")
private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
if (mService != null) {
return mService;
@@ -672,7 +673,7 @@
return mService;
}
- @GuardedBy("this")
+ @GuardedBy("sLock")
private IStatsCompanionService getIStatsCompanionServiceLocked() {
if (mStatsCompanion != null) {
return mStatsCompanion;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1829f74..601b658 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1137,7 +1137,8 @@
registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
new CachedServiceFetcher<PermissionManager>() {
@Override
- public PermissionManager createService(ContextImpl ctx) {
+ public PermissionManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
IPackageManager packageManager = AppGlobals.getPackageManager();
return new PermissionManager(ctx.getOuterContext(), packageManager);
}});
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 00903c4..63bc40b 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -55,6 +55,14 @@
static final String TAG = "DeviceAdminInfo";
/**
+ * A type of policy that this device admin can use: profile owner on an organization-owned
+ * device.
+ *
+ * @hide
+ */
+ public static final int USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER = -3;
+
+ /**
* A type of policy that this device admin can use: device owner meta-policy
* for an admin that is designated as owner of the device.
*
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 39dc51e..34ceb08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9135,7 +9135,14 @@
}
/**
- * Called by device owner to get the MAC address of the Wi-Fi device.
+ * Called by device owner, or profile owner on organization-owned device, to get the MAC
+ * address of the Wi-Fi device.
+ *
+ * NOTE: The MAC address returned here should only be used for inventory management and is
+ * not likely to be the MAC address used by the device to connect to Wi-Fi networks: MAC
+ * addresses used for scanning and connecting to Wi-Fi networks are randomized by default.
+ * To get the randomized MAC address used, call
+ * {@link android.net.wifi.WifiConfiguration#getRandomizedMacAddress}.
*
* @param admin Which device owner this request is associated with.
* @return the MAC address of the Wi-Fi device, or null when the information is not available.
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 26ff0c1..f519145 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -38,7 +38,7 @@
/**
* @hide
*/
- @IntDef({ACTION_LAUNCH, ACTION_DISMISS, ACTION_PIN})
+ @IntDef({ACTION_LAUNCH, ACTION_DISMISS, ACTION_PIN, ACTION_UNPIN})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionType {}
@@ -57,6 +57,11 @@
*/
public static final int ACTION_PIN = 3;
+ /**
+ * Event type constant indicating an app target has been un-pinned.
+ */
+ public static final int ACTION_UNPIN = 4;
+
private final AppTarget mTarget;
private final String mLocation;
private final int mAction;
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index e7d619a..471606da 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -85,7 +85,8 @@
@NonNull
public List<String> getDebugInfo() {
- return Collections.unmodifiableList(mDebugInfo);
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
}
/**
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 233dbbc..dd02af7 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -23,7 +23,6 @@
import android.util.TimestampedValue;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -52,20 +51,25 @@
};
private final int mPhoneId;
- @Nullable private TimestampedValue<Long> mUtcTime;
+ @Nullable private final TimestampedValue<Long> mUtcTime;
@Nullable private ArrayList<String> mDebugInfo;
- public PhoneTimeSuggestion(int phoneId) {
- mPhoneId = phoneId;
+ private PhoneTimeSuggestion(Builder builder) {
+ mPhoneId = builder.mPhoneId;
+ mUtcTime = builder.mUtcTime;
+ mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
}
private static PhoneTimeSuggestion createFromParcel(Parcel in) {
int phoneId = in.readInt();
- PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId);
- suggestion.setUtcTime(in.readParcelable(null /* classLoader */));
+ PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(phoneId)
+ .setUtcTime(in.readParcelable(null /* classLoader */))
+ .build();
@SuppressWarnings("unchecked")
ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
- suggestion.mDebugInfo = debugInfo;
+ if (debugInfo != null) {
+ suggestion.addDebugInfo(debugInfo);
+ }
return suggestion;
}
@@ -85,10 +89,6 @@
return mPhoneId;
}
- public void setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
- mUtcTime = utcTime;
- }
-
@Nullable
public TimestampedValue<Long> getUtcTime() {
return mUtcTime;
@@ -96,7 +96,8 @@
@NonNull
public List<String> getDebugInfo() {
- return Collections.unmodifiableList(mDebugInfo);
+ return mDebugInfo == null
+ ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
}
/**
@@ -104,11 +105,23 @@
* information is present in {@link #toString()} but is not considered for
* {@link #equals(Object)} and {@link #hashCode()}.
*/
- public void addDebugInfo(String... debugInfos) {
+ public void addDebugInfo(String debugInfo) {
if (mDebugInfo == null) {
mDebugInfo = new ArrayList<>();
}
- mDebugInfo.addAll(Arrays.asList(debugInfos));
+ 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
@@ -137,4 +150,39 @@
+ ", mDebugInfo=" + mDebugInfo
+ '}';
}
+
+ /**
+ * Builds {@link PhoneTimeSuggestion} instances.
+ *
+ * @hide
+ */
+ public static class Builder {
+ private final int mPhoneId;
+ private TimestampedValue<Long> mUtcTime;
+ private List<String> mDebugInfo;
+
+ public Builder(int phoneId) {
+ mPhoneId = phoneId;
+ }
+
+ /** Returns the builder for call chaining. */
+ public Builder setUtcTime(TimestampedValue<Long> utcTime) {
+ mUtcTime = utcTime;
+ 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;
+ }
+
+ /** Returns the {@link PhoneTimeSuggestion}. */
+ public PhoneTimeSuggestion build() {
+ return new PhoneTimeSuggestion(this);
+ }
+ }
}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 909cbc2..387a36b 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -17,6 +17,7 @@
package android.app.timezonedetector;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.os.RemoteException;
@@ -26,10 +27,11 @@
/**
* The interface through which system components can send signals to the TimeZoneDetectorService.
+ *
* @hide
*/
@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
-public final class TimeZoneDetector {
+public class TimeZoneDetector {
private static final String TAG = "timezonedetector.TimeZoneDetector";
private static final boolean DEBUG = false;
@@ -41,10 +43,11 @@
}
/**
- * 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.
+ * Suggests the current time zone, determined using telephony signals, to the detector. The
+ * detector may ignore the signal based on system settings, whether better information is
+ * available, and so on.
*/
+ @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
if (DEBUG) {
Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
@@ -56,4 +59,28 @@
}
}
+ /**
+ * Suggests the current time zone, determined for the user's manually information, to the
+ * detector. The detector may ignore the signal based on system settings.
+ */
+ @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+ public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+ if (DEBUG) {
+ Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion);
+ }
+ try {
+ mITimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * A shared utility method to create a {@link ManualTimeZoneSuggestion}.
+ */
+ public static ManualTimeZoneSuggestion createManualTimeZoneSuggestion(String tzId, String why) {
+ ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(tzId);
+ suggestion.addDebugInfo(why);
+ return suggestion;
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index accdd8d..8ed61b6 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -610,7 +610,7 @@
if (uuids == null) return false;
for (ParcelUuid uuid : uuids) {
- if (BluetoothUuid.isAvrcpTarget(uuid)) {
+ if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) {
return true;
}
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index ea3831a..0955b10 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -559,7 +559,7 @@
*
* <p> The device should already be paired.
* Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
- * {@link BluetoothProfile#PRIORITY_OFF},
+ * {@link BluetoothProfile#PRIORITY_OFF}
*
* @param device Paired bluetooth device
* @param priority
@@ -1133,8 +1133,9 @@
* is active.
* @hide
*/
+ @SystemApi
+ @Nullable
@RequiresPermission(android.Manifest.permission.BLUETOOTH)
- @UnsupportedAppUsage
public BluetoothDevice getActiveDevice() {
if (VDBG) {
Log.d(TAG, "getActiveDevice");
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index a8e1fd2..7ee29ff 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -557,7 +557,7 @@
* Set priority of the profile
*
* <p> The device should already be paired.
- * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
+ * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
*
* @param device Paired bluetooth device
* @param priority
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index bc3c9a9..7e96c23 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -16,8 +16,10 @@
package android.bluetooth;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
import android.os.ParcelUuid;
import java.nio.ByteBuffer;
@@ -31,6 +33,7 @@
*
* @hide
*/
+@SystemApi
public final class BluetoothUuid {
/* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
@@ -39,167 +42,157 @@
* The following 128 bit values are calculated as:
* uuid * 2^96 + BASE_UUID
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public static final ParcelUuid AudioSink =
+
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid A2DP_SINK =
ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
- public static final ParcelUuid AudioSource =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid A2DP_SOURCE =
ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public static final ParcelUuid AdvAudioDist =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid ADV_AUDIO_DIST =
ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid HSP =
ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid HSP_AG =
ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public static final ParcelUuid Handsfree =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid HFP =
ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
- public static final ParcelUuid Handsfree_AG =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid HFP_AG =
ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
- public static final ParcelUuid AvrcpController =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid AVRCP_CONTROLLER =
ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
- public static final ParcelUuid AvrcpTarget =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid AVRCP_TARGET =
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage
- public static final ParcelUuid ObexObjectPush =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid OBEX_OBJECT_PUSH =
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
- public static final ParcelUuid Hid =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid HID =
ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
- @UnsupportedAppUsage
- public static final ParcelUuid Hogp =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid HOGP =
ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid PANU =
ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid NAP =
ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BNEP =
ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid PBAP_PCE =
ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
- @UnsupportedAppUsage
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid PBAP_PSE =
ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid MAP =
ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid MNS =
ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid MAS =
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
- public static final ParcelUuid HearingAid =
+ /** @hide */
+ @NonNull
+ @SystemApi
+ public static final ParcelUuid HEARING_AID =
ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
+ /** @hide */
+ @NonNull
+ @SystemApi
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
- /** Length of bytes for 16 bit UUID */
- public static final int UUID_BYTES_16_BIT = 2;
- /** Length of bytes for 32 bit UUID */
- public static final int UUID_BYTES_32_BIT = 4;
- /** Length of bytes for 128 bit UUID */
- public static final int UUID_BYTES_128_BIT = 16;
-
- @UnsupportedAppUsage
- public static final ParcelUuid[] RESERVED_UUIDS = {
- AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
- ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
-
- @UnsupportedAppUsage
- public static boolean isAudioSource(ParcelUuid uuid) {
- return uuid.equals(AudioSource);
- }
-
- public static boolean isAudioSink(ParcelUuid uuid) {
- return uuid.equals(AudioSink);
- }
-
- @UnsupportedAppUsage
- public static boolean isAdvAudioDist(ParcelUuid uuid) {
- return uuid.equals(AdvAudioDist);
- }
-
- public static boolean isHandsfree(ParcelUuid uuid) {
- return uuid.equals(Handsfree);
- }
-
- public static boolean isHeadset(ParcelUuid uuid) {
- return uuid.equals(HSP);
- }
-
- public static boolean isAvrcpController(ParcelUuid uuid) {
- return uuid.equals(AvrcpController);
- }
-
- @UnsupportedAppUsage
- public static boolean isAvrcpTarget(ParcelUuid uuid) {
- return uuid.equals(AvrcpTarget);
- }
-
- public static boolean isInputDevice(ParcelUuid uuid) {
- return uuid.equals(Hid);
- }
-
- public static boolean isPanu(ParcelUuid uuid) {
- return uuid.equals(PANU);
- }
-
- public static boolean isNap(ParcelUuid uuid) {
- return uuid.equals(NAP);
- }
-
- public static boolean isBnep(ParcelUuid uuid) {
- return uuid.equals(BNEP);
- }
-
- public static boolean isMap(ParcelUuid uuid) {
- return uuid.equals(MAP);
- }
-
- public static boolean isMns(ParcelUuid uuid) {
- return uuid.equals(MNS);
- }
-
- public static boolean isMas(ParcelUuid uuid) {
- return uuid.equals(MAS);
- }
-
- public static boolean isSap(ParcelUuid uuid) {
- return uuid.equals(SAP);
- }
-
/**
- * Returns true if ParcelUuid is present in uuidArray
+ * Length of bytes for 16 bit UUID
*
- * @param uuidArray - Array of ParcelUuids
- * @param uuid
+ * @hide
*/
- @UnsupportedAppUsage
- public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
- if ((uuidArray == null || uuidArray.length == 0) && uuid == null) {
- return true;
- }
-
- if (uuidArray == null) {
- return false;
- }
-
- for (ParcelUuid element : uuidArray) {
- if (element.equals(uuid)) return true;
- }
- return false;
- }
+ @SystemApi
+ public static final int UUID_BYTES_16_BIT = 2;
+ /**
+ * Length of bytes for 32 bit UUID
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int UUID_BYTES_32_BIT = 4;
+ /**
+ * Length of bytes for 128 bit UUID
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int UUID_BYTES_128_BIT = 16;
/**
* Returns true if there any common ParcelUuids in uuidA and uuidB.
*
* @param uuidA - List of ParcelUuids
* @param uuidB - List of ParcelUuids
+ *
+ * @hide
*/
- @UnsupportedAppUsage
- public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
+ @SystemApi
+ public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA,
+ @Nullable ParcelUuid[] uuidB) {
if (uuidA == null && uuidB == null) return true;
if (uuidA == null) {
@@ -218,29 +211,6 @@
}
/**
- * Returns true if all the ParcelUuids in ParcelUuidB are present in
- * ParcelUuidA
- *
- * @param uuidA - Array of ParcelUuidsA
- * @param uuidB - Array of ParcelUuidsB
- */
- public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
- if (uuidA == null && uuidB == null) return true;
-
- if (uuidA == null) {
- return uuidB.length == 0;
- }
-
- if (uuidB == null) return true;
-
- HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA));
- for (ParcelUuid uuid : uuidB) {
- if (!uuidSet.contains(uuid)) return false;
- }
- return true;
- }
-
- /**
* Extract the Service Identifier or the actual uuid from the Parcel Uuid.
* For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
* this function will return 110B
@@ -248,7 +218,7 @@
* @param parcelUuid
* @return the service identifier.
*/
- public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
+ private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32;
return (int) value;
@@ -262,8 +232,12 @@
* @param uuidBytes Byte representation of uuid.
* @return {@link ParcelUuid} parsed from bytes.
* @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
+ *
+ * @hide
*/
- public static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
+ @NonNull
+ @SystemApi
+ public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) {
if (uuidBytes == null) {
throw new IllegalArgumentException("uuidBytes cannot be null");
}
@@ -305,6 +279,8 @@
* @param uuid uuid to parse.
* @return shortest representation of {@code uuid} as bytes.
* @throws IllegalArgumentException If the {@code uuid} is null.
+ *
+ * @hide
*/
public static byte[] uuidToBytes(ParcelUuid uuid) {
if (uuid == null) {
@@ -345,6 +321,8 @@
*
* @param parcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static boolean is16BitUuid(ParcelUuid parcelUuid) {
@@ -361,6 +339,8 @@
*
* @param parcelUuid
* @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
+ *
+ * @hide
*/
@UnsupportedAppUsage
public static boolean is32BitUuid(ParcelUuid parcelUuid) {
@@ -373,4 +353,6 @@
}
return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
}
+
+ private BluetoothUuid() {}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 341b520..d370a38 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3427,7 +3427,6 @@
//@hide: TIME_DETECTOR_SERVICE,
//@hide: TIME_ZONE_DETECTOR_SERVICE,
PERMISSION_SERVICE,
- INCREMENTAL_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -4971,6 +4970,13 @@
/**
* Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.content.pm.DataLoaderManager}.
+ * @hide
+ */
+ public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve an
* {@link android.os.incremental.IncrementalManager}.
* @hide
*/
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 40aca0e..af7b986 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4053,6 +4053,13 @@
public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
/**
+ * Used for looking up a Data Loader Service providers.
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
+
+ /**
* An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration
* state.
* @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY
diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java
new file mode 100644
index 0000000..2688038
--- /dev/null
+++ b/core/java/android/content/pm/DataLoaderManager.java
@@ -0,0 +1,87 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * Data loader manager takes care of data loaders of different packages. It provides methods to
+ * initialize a data loader binder service (binding and creating it), to return a binder of the data
+ * loader binder service and to destroy a data loader binder service.
+ * @see com.android.server.pm.DataLoaderManagerService
+ * @hide
+ */
+public class DataLoaderManager {
+ private static final String TAG = "DataLoaderManager";
+ private final IDataLoaderManager mService;
+
+ public DataLoaderManager(IDataLoaderManager service) {
+ mService = service;
+ }
+
+ /**
+ * Finds a data loader binder service and binds to it. This requires PackageManager.
+ *
+ * @param dataLoaderId ID for the new data loader binder service.
+ * @param params Bundle that contains parameters to configure the data loader service.
+ * Must contain:
+ * key: "packageName", value: String, package name of data loader service
+ * package;
+ * key: "extras", value: Bundle, client-specific data structures
+ *
+ * @param listener Callback for the data loader service to report status back to the
+ * caller.
+ * @return false if 1) target ID collides with a data loader that is already bound to data
+ * loader manager; 2) package name is not specified; 3) fails to find data loader package;
+ * or 4) fails to bind to the specified data loader service, otherwise return true.
+ */
+ public boolean initializeDataLoader(int dataLoaderId, @NonNull Bundle params,
+ @NonNull IDataLoaderStatusListener listener) {
+ try {
+ return mService.initializeDataLoader(dataLoaderId, params, listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a binder interface of the data loader binder service, given its ID.
+ */
+ @Nullable
+ public IDataLoader getDataLoader(int dataLoaderId) {
+ try {
+ return mService.getDataLoader(dataLoaderId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Destroys the data loader binder service and removes it from data loader manager service.
+ */
+ @Nullable
+ public void destroyDataLoader(int dataLoaderId) {
+ try {
+ mService.destroyDataLoader(dataLoaderId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl
new file mode 100644
index 0000000..60cc9ba9
--- /dev/null
+++ b/core/java/android/content/pm/IDataLoader.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.content.pm;
+
+import android.os.Bundle;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.InstallationFile;
+import java.util.List;
+
+/**
+ * TODO: update with new APIs
+ * @hide
+ */
+oneway interface IDataLoader {
+ void create(int id, in Bundle params, IDataLoaderStatusListener listener);
+ void start(in List<InstallationFile> fileInfos);
+ void stop();
+ void destroy();
+ void onFileCreated(long inode, in byte[] metadata);
+}
diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl
new file mode 100644
index 0000000..f453c9b
--- /dev/null
+++ b/core/java/android/content/pm/IDataLoaderManager.aidl
@@ -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.content.pm;
+
+import android.os.Bundle;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
+import java.util.List;
+
+/** @hide */
+interface IDataLoaderManager {
+ boolean initializeDataLoader(int id, in Bundle params, IDataLoaderStatusListener listener);
+ IDataLoader getDataLoader(int dataLoaderId);
+ void destroyDataLoader(int dataLoaderId);
+}
\ No newline at end of file
diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
similarity index 80%
rename from core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl
rename to core/java/android/content/pm/IDataLoaderStatusListener.aidl
index f04242d..a60d6ee 100644
--- a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.service.incremental;
+package android.content.pm;
/**
- * Callbacks from DataLoaderService to IncrementalService to report data loader status.
+ * Callbacks from a data loader binder service to report data loader status.
* @hide
*/
-oneway interface IIncrementalDataLoaderStatusListener {
+oneway interface IDataLoaderStatusListener {
/** Data loader status */
const int DATA_LOADER_READY = 0;
const int DATA_LOADER_NOT_READY = 1;
@@ -31,6 +31,6 @@
const int DATA_LOADER_CONNECTION_OK = 6;
/** Data loader status callback */
- void onStatusChanged(in int storageId, in int status);
+ void onStatusChanged(in int dataLoaderId, in int status);
}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index b740617..0b3c765 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -46,4 +46,5 @@
int getParentSessionId();
boolean isStaged();
+ void addFile(in String name, long size, in byte[] metadata);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a71367d..b3d8eb5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,6 +26,7 @@
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
@@ -237,6 +238,8 @@
@UnsupportedAppUsage
String getInstallerPackageName(in String packageName);
+ InstallSourceInfo getInstallSourceInfo(in String packageName);
+
void resetApplicationPreferences(int userId);
@UnsupportedAppUsage
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/content/pm/InstallSourceInfo.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/content/pm/InstallSourceInfo.aidl
index 007ec94..21ee4c3 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/content/pm/InstallSourceInfo.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.content.pm;
-parcelable WifiActivityEnergyInfo;
+parcelable InstallSourceInfo;
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
new file mode 100644
index 0000000..4d235f1
--- /dev/null
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -0,0 +1,110 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about how an app was installed.
+ * @see PackageManager#getInstallSourceInfo(String)
+ */
+public final class InstallSourceInfo implements Parcelable {
+
+ @Nullable private final String mInitiatingPackageName;
+
+ @Nullable private final String mOriginatingPackageName;
+
+ @Nullable private final String mInstallingPackageName;
+
+ /** @hide */
+ public InstallSourceInfo(@Nullable String initiatingPackageName,
+ @Nullable String originatingPackageName, @Nullable String installingPackageName) {
+ this.mInitiatingPackageName = initiatingPackageName;
+ this.mOriginatingPackageName = originatingPackageName;
+ this.mInstallingPackageName = installingPackageName;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mInitiatingPackageName);
+ dest.writeString(mOriginatingPackageName);
+ dest.writeString(mInstallingPackageName);
+ }
+
+ private InstallSourceInfo(Parcel source) {
+ mInitiatingPackageName = source.readString();
+ mOriginatingPackageName = source.readString();
+ mInstallingPackageName = source.readString();
+ }
+
+ /** The name of the package that requested the installation, or null if not available. */
+ @Nullable
+ public String getInitiatingPackageName() {
+ return mInitiatingPackageName;
+ }
+
+ /**
+ * The name of the package on behalf of which the initiating package requested the installation,
+ * or null if not available.
+ * <p>
+ * For example if a downloaded APK is installed via the Package Installer this could be the
+ * app that performed the download. This value is provided by the initiating package and not
+ * verified by the framework.
+ * <p>
+ * Note that the {@code InstallSourceInfo} returned by
+ * {@link PackageManager#getInstallSourceInfo(String)} will not have this information
+ * available unless the calling application holds the INSTALL_PACKAGES permission.
+ */
+ @Nullable
+ public String getOriginatingPackageName() {
+ return mOriginatingPackageName;
+ }
+
+ /**
+ * The name of the package responsible for the installation (the installer of record), or null
+ * if not available.
+ * Note that this may differ from the initiating package name and can be modified.
+ *
+ * @see PackageManager#setInstallerPackageName(String, String)
+ */
+ @Nullable
+ public String getInstallingPackageName() {
+ return mInstallingPackageName;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<InstallSourceInfo> CREATOR =
+ new Creator<InstallSourceInfo>() {
+ @Override
+ public InstallSourceInfo createFromParcel(Parcel source) {
+ return new InstallSourceInfo(source);
+ }
+
+ @Override
+ public InstallSourceInfo[] newArray(int size) {
+ return new InstallSourceInfo[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/content/pm/InstallationFile.aidl
similarity index 68%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/content/pm/InstallationFile.aidl
index 007ec94..1edff9d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/content/pm/InstallationFile.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,10 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.content.pm;
-parcelable WifiActivityEnergyInfo;
+/**
+ * Describes a file which is part of a package installation.
+ */
+parcelable InstallationFile;
+
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
new file mode 100644
index 0000000..ac5fd1e
--- /dev/null
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -0,0 +1,118 @@
+/*
+ * 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.content.pm;
+
+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;
+
+/**
+ * Defines the properties of a file in an installation session.
+ * TODO(b/136132412): update with new APIs.
+ *
+ * @hide
+ */
+public final class InstallationFile implements Parcelable {
+ public static final int FILE_TYPE_UNKNOWN = -1;
+ public static final int FILE_TYPE_APK = 0;
+ public static final int FILE_TYPE_LIB = 1;
+ public static final int FILE_TYPE_OBB = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"FILE_TYPE_"}, value = {
+ FILE_TYPE_APK,
+ FILE_TYPE_LIB,
+ FILE_TYPE_OBB,
+ })
+ public @interface FileType {
+ }
+
+ private String mFileName;
+ private @FileType int mFileType;
+ private long mFileSize;
+ private byte[] mMetadata;
+
+ public InstallationFile(@NonNull String fileName, long fileSize,
+ @Nullable byte[] metadata) {
+ mFileName = fileName;
+ mFileSize = fileSize;
+ mMetadata = metadata;
+ if (fileName.toLowerCase().endsWith(".apk")) {
+ mFileType = FILE_TYPE_APK;
+ } else if (fileName.toLowerCase().endsWith(".obb")) {
+ mFileType = FILE_TYPE_OBB;
+ } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
+ "lib/")) {
+ mFileType = FILE_TYPE_LIB;
+ } else {
+ mFileType = FILE_TYPE_UNKNOWN;
+ }
+ }
+
+ public @FileType int getFileType() {
+ return mFileType;
+ }
+
+ public @NonNull String getName() {
+ return mFileName;
+ }
+
+ public long getSize() {
+ return mFileSize;
+ }
+
+ public @Nullable byte[] getMetadata() {
+ return mMetadata;
+ }
+
+ private InstallationFile(Parcel source) {
+ mFileName = source.readString();
+ mFileType = source.readInt();
+ mFileSize = source.readLong();
+ mMetadata = source.createByteArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mFileName);
+ dest.writeInt(mFileType);
+ dest.writeLong(mFileSize);
+ dest.writeByteArray(mMetadata);
+ }
+
+ public static final @NonNull Creator<InstallationFile> CREATOR =
+ new Creator<InstallationFile>() {
+ public InstallationFile createFromParcel(Parcel source) {
+ return new InstallationFile(source);
+ }
+
+ public InstallationFile[] newArray(int size) {
+ return new InstallationFile[size];
+ }
+ };
+
+}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4ab6f3c..e9fc8f6 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -50,6 +50,8 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.incremental.IncrementalDataLoaderParams;
+import android.os.incremental.IncrementalDataLoaderParamsParcel;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArraySet;
@@ -1212,6 +1214,27 @@
}
/**
+ * Configure files for an installation session.
+ *
+ * Currently only for Incremental installation session. Once this method is called,
+ * the files and their paths, as specified in the parameters, will be created and properly
+ * configured in the Incremental File System.
+ *
+ * TODO(b/136132412): update this and InstallationFile class with latest API design.
+ *
+ * @throws IllegalStateException if {@link SessionParams#incrementalParams} is null.
+ *
+ * @hide
+ */
+ public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
+ try {
+ mSession.addFile(name, size, metadata);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Release this session object. You can open the session again if it
* hasn't been finalized.
*/
@@ -1398,6 +1421,8 @@
public boolean isStaged;
/** {@hide} */
public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
+ /** {@hide} */
+ public IncrementalDataLoaderParams incrementalParams;
/**
* Construct parameters for a new package install session.
@@ -1431,6 +1456,12 @@
isMultiPackage = source.readBoolean();
isStaged = source.readBoolean();
requiredInstalledVersionCode = source.readLong();
+ IncrementalDataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
+ IncrementalDataLoaderParamsParcel.class.getClassLoader());
+ if (dataLoaderParamsParcel != null) {
+ incrementalParams = new IncrementalDataLoaderParams(
+ dataLoaderParamsParcel);
+ }
}
/** {@hide} */
@@ -1454,6 +1485,7 @@
ret.isMultiPackage = isMultiPackage;
ret.isStaged = isStaged;
ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
+ ret.incrementalParams = incrementalParams;
return ret;
}
@@ -1782,6 +1814,16 @@
return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
}
+ /**
+ * Set Incremental data loader params.
+ *
+ * {@hide}
+ */
+ @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+ public void setIncrementalParams(@NonNull IncrementalDataLoaderParams incrementalParams) {
+ this.incrementalParams = incrementalParams;
+ }
+
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
@@ -1831,6 +1873,11 @@
dest.writeBoolean(isMultiPackage);
dest.writeBoolean(isStaged);
dest.writeLong(requiredInstalledVersionCode);
+ if (incrementalParams != null) {
+ dest.writeParcelable(incrementalParams.getData(), flags);
+ } else {
+ dest.writeParcelable(null, flags);
+ }
}
public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bbfdf91..6e890ba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5989,11 +5989,30 @@
*
* @param packageName The name of the package to query
* @throws IllegalArgumentException if the given package name is not installed
+ *
+ * @deprecated use {@link #getInstallSourceInfo(String)} instead
*/
+ @Deprecated
@Nullable
public abstract String getInstallerPackageName(@NonNull String packageName);
/**
+ * Retrieves information about how a package was installed or updated.
+ * <p>
+ * If the calling application does not hold the INSTALL_PACKAGES permission then
+ * the result will always return {@code null} from
+ * {@link InstallSourceInfo#getOriginatingPackageName()}.
+ *
+ * @param packageName The name of the package to query
+ * @throws NameNotFoundException if the given package name is not installed
+ */
+ @NonNull
+ public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName)
+ throws NameNotFoundException {
+ throw new UnsupportedOperationException("getInstallSourceInfo not implemented");
+ }
+
+ /**
* Attempts to clear the user data directory of an application.
* Since this may take a little while, the result will
* be posted back to the given observer. A deletion will fail if the
diff --git a/core/java/android/content/res/loader/ResourceLoaderManager.java b/core/java/android/content/res/loader/ResourceLoaderManager.java
index ddbfa81..592ec09 100644
--- a/core/java/android/content/res/loader/ResourceLoaderManager.java
+++ b/core/java/android/content/res/loader/ResourceLoaderManager.java
@@ -151,6 +151,7 @@
public void onImplUpdate(ResourcesImpl resourcesImpl) {
synchronized (mLock) {
this.mResourcesImpl = resourcesImpl;
+ this.mResourcesImpl.getAssets().setResourceLoaderManager(this);
updateLoaders();
}
}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/net/MacAddress.aidl
similarity index 80%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/net/MacAddress.aidl
index 007ec94..48a18a7 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/net/MacAddress.aidl
@@ -1,5 +1,6 @@
/**
- * Copyright (c) 2014, The Android Open Source Project
+ *
+ * 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.
@@ -14,6 +15,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.net;
-parcelable WifiActivityEnergyInfo;
+@JavaOnlyStableParcelable parcelable MacAddress;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3f56def..f12ba13 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -63,7 +63,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public final int netId;
// Objects used to perform per-network operations such as getSocketFactory
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 1c6a484..bf4884a 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -595,8 +596,15 @@
return total;
}
- /** {@hide} */
- public static long getTxPackets(String iface) {
+ /**
+ * Return the number of packets transmitted on the specified interface since
+ * device boot. Statistics are measured at the network layer, so both TCP and
+ * UDP usage are included.
+ *
+ * @param iface The name of the interface.
+ * @return The number of transmitted packets.
+ */
+ public static long getTxPackets(@NonNull String iface) {
try {
return getStatsService().getIfaceStats(iface, TYPE_TX_PACKETS);
} catch (RemoteException e) {
@@ -604,8 +612,15 @@
}
}
- /** {@hide} */
- public static long getRxPackets(String iface) {
+ /**
+ * Return the number of packets received on the specified interface since
+ * device boot. Statistics are measured at the network layer, so both TCP
+ * and UDP usage are included.
+ *
+ * @param iface The name of the interface.
+ * @return The number of received packets.
+ */
+ public static long getRxPackets(@NonNull String iface) {
try {
return getStatsService().getIfaceStats(iface, TYPE_RX_PACKETS);
} catch (RemoteException e) {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 40048d9..e81a505 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -87,6 +87,7 @@
void setDefaultGuestRestrictions(in Bundle restrictions);
Bundle getDefaultGuestRestrictions();
boolean markGuestForDeletion(int userId);
+ UserInfo findCurrentGuestUser();
boolean isQuietModeEnabled(int userId);
void setSeedAccountData(int userId, in String accountName,
in String accountType, in PersistableBundle accountOptions, boolean persist);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dbe8dc3..9e9cd92 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2375,6 +2375,20 @@
}
/**
+ * Gets the existing guest user if it exists. This does not include guest users that are dying.
+ * @return The existing guest user if it exists. Null otherwise.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ public UserInfo findCurrentGuestUser() {
+ try {
+ return mService.findCurrentGuestUser();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a user with the specified name and options as a profile of another user.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
* The type of profile must be specified using the given flags.
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
similarity index 94%
rename from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
rename to core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
index 007ec94..ce75c12 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.os.connectivity;
parcelable WifiActivityEnergyInfo;
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
similarity index 99%
rename from wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
rename to core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index f0f31fa..7db003d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.os.connectivity;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
index 12740ea..ffff52e 100644
--- a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
+++ b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
@@ -18,7 +18,7 @@
import android.os.incremental.IncrementalFileSystemControlParcel;
import android.os.incremental.IncrementalDataLoaderParamsParcel;
-import android.service.incremental.IIncrementalDataLoaderStatusListener;
+import android.content.pm.IDataLoaderStatusListener;
/**
* Binder service to receive calls from native Incremental Service and handle Java tasks such as
@@ -29,7 +29,7 @@
boolean prepareDataLoader(int mountId,
in IncrementalFileSystemControlParcel control,
in IncrementalDataLoaderParamsParcel params,
- in IIncrementalDataLoaderStatusListener listener);
+ in IDataLoaderStatusListener listener);
boolean startDataLoader(int mountId);
void showHealthBlockedUI(int mountId);
void destroyDataLoader(int mountId);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
new file mode 100644
index 0000000..5bd0748
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -0,0 +1,280 @@
+/*
+ * 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;
+
+/**
+ * Set up files and directories used in an installation session.
+ * Currently only used by Incremental Installation.
+ * For Incremental installation, the expected outcome of this function is:
+ * 0) All the files are in defaultStorage
+ * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
+ * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
+ * 2) All lib files are in the sub directories as their names suggest, and in the same parent
+ * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound
+ * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ *
+ * @throws IllegalStateException the session is not an Incremental installation session.
+ */
+
+import static dalvik.system.VMRuntime.getInstructionSet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.InstallationFile;
+import android.os.IVold;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Random;
+
+/**
+ * This class manages storage instances used during a package installation session.
+ * @hide
+ */
+public final class IncrementalFileStorages {
+ private static final String TAG = "IncrementalFileStorages";
+ private @Nullable IncrementalStorage mDefaultStorage;
+ private @Nullable IncrementalStorage mApkStorage;
+ private @Nullable IncrementalStorage mObbStorage;
+ private @Nullable String mDefaultDir;
+ private @Nullable String mObbDir;
+ private @NonNull IncrementalManager mIncrementalManager;
+ private @Nullable ArraySet<String> mLibDirs;
+ private @NonNull String mPackageName;
+ private @NonNull File mStageDir;
+
+ /**
+ * Set up files and directories used in an installation session.
+ * Currently only used by Incremental Installation.
+ * For Incremental installation, the expected outcome of this function is:
+ * 0) All the files are in defaultStorage
+ * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
+ * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
+ * 2) All lib files are in the sub directories as their names suggest, and in the same parent
+ * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound
+ * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ *
+ * @throws IllegalStateException the session is not an Incremental installation session.
+ */
+ public IncrementalFileStorages(@NonNull String packageName,
+ @NonNull File stageDir,
+ @NonNull IncrementalManager incrementalManager,
+ @NonNull IncrementalDataLoaderParams incrementalDataLoaderParams) {
+ mPackageName = packageName;
+ mStageDir = stageDir;
+ mIncrementalManager = incrementalManager;
+ if (incrementalDataLoaderParams.getPackageName().equals("local")) {
+ final String incrementalPath = incrementalDataLoaderParams.getStaticArgs();
+ mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
+ mDefaultDir = incrementalPath;
+ return;
+ }
+ mDefaultDir = getTempDir();
+ if (mDefaultDir == null) {
+ return;
+ }
+ mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
+ incrementalDataLoaderParams,
+ IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
+ }
+
+ /**
+ * Adds a file into the installation session. Makes sure it will be placed inside
+ * a proper storage instance, based on its file type.
+ */
+ public void addFile(@NonNull InstallationFile file) throws IOException {
+ if (mDefaultStorage == null) {
+ throw new IOException("Cannot add file because default storage does not exist");
+ }
+ if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+ addApkFile(file);
+ } else if (file.getFileType() == InstallationFile.FILE_TYPE_OBB) {
+ addObbFile(file);
+ } else if (file.getFileType() == InstallationFile.FILE_TYPE_LIB) {
+ addLibFile(file);
+ } else {
+ throw new IOException("Unknown file type: " + file.getFileType());
+ }
+ }
+
+ private void addApkFile(@NonNull InstallationFile apk) throws IOException {
+ // Create a storage for APK files and lib files
+ final String stageDirPath = mStageDir.getAbsolutePath();
+ if (mApkStorage == null) {
+ mApkStorage = mIncrementalManager.createStorage(stageDirPath, mDefaultStorage,
+ IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+ mApkStorage.bind(stageDirPath);
+ }
+
+ if (!new File(mDefaultDir, apk.getName()).exists()) {
+ mDefaultStorage.makeFile(apk.getName(), apk.getSize(),
+ apk.getMetadata());
+ }
+ // Assuming APK files are already named properly, e.g., "base.apk"
+ mDefaultStorage.makeLink(apk.getName(), mApkStorage, apk.getName());
+ }
+
+ private void addLibFile(@NonNull InstallationFile lib) throws IOException {
+ // TODO(b/136132412): remove this after we have incfs support for lib file mapping
+ if (mApkStorage == null) {
+ throw new IOException("Cannot add lib file without adding an apk file first");
+ }
+ if (mLibDirs == null) {
+ mLibDirs = new ArraySet<>();
+ }
+ String current = "";
+ final Path libDirPath = Paths.get(lib.getName()).getParent();
+ final int numDirComponents = libDirPath.getNameCount();
+ for (int i = 0; i < numDirComponents; i++) {
+ String dirName = libDirPath.getName(i).toString();
+ try {
+ dirName = getInstructionSet(dirName);
+ } catch (IllegalArgumentException ignored) {
+ }
+ current += dirName;
+ if (!mLibDirs.contains(current)) {
+ mDefaultStorage.makeDirectory(current);
+ mApkStorage.makeDirectory(current);
+ mLibDirs.add(current);
+ }
+ current += '/';
+ }
+ String libFilePath = current + Paths.get(lib.getName()).getFileName();
+ mDefaultStorage.makeFile(libFilePath, lib.getSize(), lib.getMetadata());
+ mDefaultStorage.makeLink(libFilePath, mApkStorage, libFilePath);
+ }
+
+ private void addObbFile(@NonNull InstallationFile obb) throws IOException {
+ if (mObbStorage == null) {
+ // Create a storage for OBB files
+ mObbDir = getTempDir();
+ if (mObbDir == null) {
+ throw new IOException("Failed to create obb storage directory.");
+ }
+ mObbStorage = mIncrementalManager.createStorage(
+ mObbDir, mDefaultStorage,
+ IncrementalManager.CREATE_MODE_CREATE
+ | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+ }
+ mDefaultStorage.makeFile(obb.getName(), obb.getSize(), obb.getMetadata());
+ mDefaultStorage.makeLink(obb.getName(), mObbStorage, obb.getName());
+ }
+
+ private boolean hasObb() {
+ return (mObbStorage != null && mObbDir != null);
+ }
+
+ /**
+ * Starts loading data for default storage.
+ * TODO(b/136132412): update the implementation with latest API design.
+ */
+ public boolean startLoading() {
+ if (mDefaultStorage == null) {
+ return false;
+ }
+ return mDefaultStorage.startLoading();
+ }
+
+ /**
+ * Sets up obb storage directory and create bindings.
+ */
+ public void finishSetUp() {
+ if (!hasObb()) {
+ return;
+ }
+ final String mainObbDir = String.format("/storage/emulated/0/Android/obb/%s", mPackageName);
+ final String packageObbDirRoot =
+ String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName);
+ final String[] obbDirs = {
+ packageObbDirRoot + "read",
+ packageObbDirRoot + "write",
+ packageObbDirRoot + "full",
+ packageObbDirRoot + "default",
+ String.format("/data/media/0/Android/obb/%s", mPackageName),
+ mainObbDir,
+ };
+ try {
+ Slog.i(TAG, "Creating obb directory '" + mainObbDir + "'");
+ final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold"));
+ vold.mkdirs(mainObbDir);
+ for (String d : obbDirs) {
+ mObbStorage.bindPermanent(d);
+ }
+ } catch (ServiceManager.ServiceNotFoundException ex) {
+ Slog.e(TAG, "vold service is not found.");
+ cleanUp();
+ } catch (IOException | RemoteException ex) {
+ Slog.e(TAG, "Failed to create obb dir at: " + mainObbDir, ex);
+ cleanUp();
+ }
+ }
+
+ /**
+ * Resets the states and unbinds storage instances for an installation session.
+ * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
+ */
+ public void cleanUp() {
+ if (mDefaultStorage != null && mDefaultDir != null) {
+ try {
+ mDefaultStorage.unBind(mDefaultDir);
+ } catch (IOException ignored) {
+ }
+ mDefaultDir = null;
+ mDefaultStorage = null;
+ }
+ if (mApkStorage != null && mStageDir != null) {
+ try {
+ mApkStorage.unBind(mStageDir.getAbsolutePath());
+ } catch (IOException ignored) {
+ }
+ mApkStorage = null;
+ }
+ if (mObbStorage != null && mObbDir != null) {
+ try {
+ mObbStorage.unBind(mObbDir);
+ } catch (IOException ignored) {
+ }
+ mObbDir = null;
+ mObbStorage = null;
+ }
+ }
+
+ private String getTempDir() {
+ final String tmpDirRoot = "/data/tmp";
+ final Random random = new Random();
+ final Path tmpDir =
+ Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1)));
+ try {
+ Files.createDirectories(tmpDir);
+ } catch (Exception ex) {
+ Slog.e(TAG, "Failed to create dir", ex);
+ return null;
+ }
+ return tmpDir.toAbsolutePath().toString();
+ }
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 6c4ee01..09286fe 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -17,6 +17,7 @@
package android.permission;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -29,6 +30,8 @@
import android.content.pm.IPackageManager;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.Immutable;
@@ -36,6 +39,8 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* System level service for accessing the permission capabilities of the platform.
@@ -59,6 +64,8 @@
private final IPackageManager mPackageManager;
+ private final IPermissionManager mPermissionManager;
+
private List<SplitPermissionInfo> mSplitPermissionInfos;
/**
@@ -67,9 +74,12 @@
* @param context The current context in which to operate.
* @hide
*/
- public PermissionManager(@NonNull Context context, IPackageManager packageManager) {
+ public PermissionManager(@NonNull Context context, IPackageManager packageManager)
+ throws ServiceManager.ServiceNotFoundException {
mContext = context;
mPackageManager = packageManager;
+ mPermissionManager = IPermissionManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow("permissionmgr"));
}
/**
@@ -145,6 +155,48 @@
return mSplitPermissionInfos;
}
+ /**
+ * Grant default permissions to currently active LUI app
+ * @param packageName The package name for the LUI app
+ * @param user The user handle
+ * @param callback The callback provided by caller to be notified when grant completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void grantDefaultPermissionsToLuiApp(
+ @NonNull String packageName, @NonNull UserHandle user,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.grantDefaultPermissionsToActiveLuiApp(
+ packageName, user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Revoke default permissions to currently active LUI app
+ * @param packageNames The package names for the LUI apps
+ * @param user The user handle
+ * @param callback The callback provided by caller to be notified when grant completes
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+ public void revokeDefaultPermissionsFromLuiApps(
+ @NonNull String[] packageNames, @NonNull UserHandle user,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+ try {
+ mPermissionManager.revokeDefaultPermissionsFromLuiApps(
+ packageNames, user.getIdentifier());
+ executor.execute(() -> callback.accept(true));
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
List<SplitPermissionInfoParcelable> parcelableList) {
final int size = parcelableList.size();
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 7995f22..1eb7664 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -16,7 +16,6 @@
package android.provider;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.WorkerThread;
import android.content.Context;
@@ -155,12 +154,7 @@
/** The authority for the blocked number provider */
public static final String AUTHORITY = "com.android.blockednumber";
- /**
- * A content:// style uri to the authority for the blocked number provider
- * @hide
- */
- @NonNull
- @SystemApi
+ /** A content:// style uri to the authority for the blocked number provider */
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
private static final String LOG_TAG = BlockedNumberContract.class.getSimpleName();
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 01f9c73..2c53025 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -20,6 +20,7 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.CurrentTimeSecondsLong;
import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -78,6 +79,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
@@ -205,8 +208,10 @@
public static final String PARAM_DELETE_DATA = "deletedata";
/** {@hide} */
+ @Deprecated
public static final String PARAM_INCLUDE_PENDING = "includePending";
/** {@hide} */
+ @Deprecated
public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
/** {@hide} */
public static final String PARAM_PROGRESS = "progress";
@@ -559,6 +564,101 @@
public static final String UNKNOWN_STRING = "<unknown>";
/**
+ * Specify a {@link Uri} that is "related" to the current operation being
+ * performed.
+ * <p>
+ * This is typically used to allow an operation that may normally be
+ * rejected, such as making a copy of a pre-existing image located under a
+ * {@link MediaColumns#RELATIVE_PATH} where new images are not allowed.
+ * <p>
+ * It's strongly recommended that when making a copy of pre-existing content
+ * that you define the "original document ID" GUID as defined by the <em>XMP
+ * Media Management</em> standard.
+ * <p>
+ * This key can be placed in a {@link Bundle} of extras and passed to
+ * {@link ContentResolver#insert}.
+ */
+ public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
+
+ /**
+ * Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
+ * performing a {@link MediaStore} operation.
+ * <p>
+ * This key can be placed in a {@link Bundle} of extras and passed to
+ * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+ * {@link ContentResolver#delete}.
+ * <p>
+ * By default, pending items are filtered away from operations.
+ */
+ @Match
+ public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
+
+ /**
+ * Specify how {@link MediaColumns#IS_TRASHED} items should be filtered when
+ * performing a {@link MediaStore} operation.
+ * <p>
+ * This key can be placed in a {@link Bundle} of extras and passed to
+ * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+ * {@link ContentResolver#delete}.
+ * <p>
+ * By default, trashed items are filtered away from operations.
+ */
+ @Match
+ public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
+
+ /**
+ * Specify how {@link MediaColumns#IS_FAVORITE} items should be filtered
+ * when performing a {@link MediaStore} operation.
+ * <p>
+ * This key can be placed in a {@link Bundle} of extras and passed to
+ * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+ * {@link ContentResolver#delete}.
+ * <p>
+ * By default, favorite items are <em>not</em> filtered away from
+ * operations.
+ */
+ @Match
+ public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "MATCH_" }, value = {
+ MATCH_DEFAULT,
+ MATCH_INCLUDE,
+ MATCH_EXCLUDE,
+ MATCH_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Match {}
+
+ /**
+ * Value indicating that the default matching behavior should be used, as
+ * defined by the key documentation.
+ */
+ public static final int MATCH_DEFAULT = 0;
+
+ /**
+ * Value indicating that operations should include items matching the
+ * criteria defined by this key.
+ * <p>
+ * Note that items <em>not</em> matching the criteria <em>may</em> also be
+ * included depending on the default behavior documented by the key. If you
+ * want to operate exclusively on matching items, use {@link #MATCH_ONLY}.
+ */
+ public static final int MATCH_INCLUDE = 1;
+
+ /**
+ * Value indicating that operations should exclude items matching the
+ * criteria defined by this key.
+ */
+ public static final int MATCH_EXCLUDE = 2;
+
+ /**
+ * Value indicating that operations should only operate on items explicitly
+ * matching the criteria defined by this key.
+ */
+ public static final int MATCH_ONLY = 3;
+
+ /**
* Update the given {@link Uri} to also include any pending media items from
* calls such as
* {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
@@ -566,12 +666,16 @@
*
* @see MediaColumns#IS_PENDING
* @see MediaStore#getIncludePending(Uri)
+ * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
+ * is more expressive.
*/
+ @Deprecated
public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
return setIncludePending(uri.buildUpon()).build();
}
/** @hide */
+ @Deprecated
public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) {
return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1");
}
@@ -582,7 +686,11 @@
*
* @see MediaColumns#IS_PENDING
* @see MediaStore#setIncludePending(Uri)
+ * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
+ * is more expressive.
+ * @removed
*/
+ @Deprecated
public static boolean getIncludePending(@NonNull Uri uri) {
return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING));
}
@@ -597,7 +705,11 @@
* @see MediaStore#setIncludeTrashed(Uri)
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
+ * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_TRASHED} which
+ * is more expressive.
+ * @removed
*/
+ @Deprecated
public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
}
@@ -1000,7 +1112,7 @@
* the field to {@code 0}, or until they expire as defined by
* {@link #DATE_EXPIRES}.
*
- * @see MediaStore#setIncludePending(Uri)
+ * @see MediaStore#QUERY_ARG_MATCH_PENDING
*/
@Column(Cursor.FIELD_TYPE_INTEGER)
public static final String IS_PENDING = "is_pending";
@@ -1011,8 +1123,7 @@
* Trashed items are retained until they expire as defined by
* {@link #DATE_EXPIRES}.
*
- * @see MediaColumns#IS_TRASHED
- * @see MediaStore#setIncludeTrashed(Uri)
+ * @see MediaStore#QUERY_ARG_MATCH_TRASHED
* @see MediaStore#trash(Context, Uri)
* @see MediaStore#untrash(Context, Uri)
*/
@@ -1186,6 +1297,8 @@
/**
* Flag indicating if the media item has been marked as being a
* "favorite" by the user.
+ *
+ * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
*/
@Column(Cursor.FIELD_TYPE_INTEGER)
public static final String IS_FAVORITE = "is_favorite";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fa8896e..ad8d553 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8782,6 +8782,22 @@
public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
/**
+ * The package name for the custom bugreport handler app. This app must be whitelisted.
+ * This is currently used only by Power Menu short press.
+ *
+ * @hide
+ */
+ public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app";
+
+ /**
+ * The user id for the custom bugreport handler app. This is currently used only by Power
+ * Menu short press.
+ *
+ * @hide
+ */
+ public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user";
+
+ /**
* Whether ADB is enabled.
*/
public static final String ADB_ENABLED = "adb_enabled";
@@ -8918,14 +8934,33 @@
* List of ISO country codes in which eUICC UI is shown. Country codes should be separated
* by comma.
*
- * <p>Used to hide eUICC UI from users who are currently in countries no carriers support
- * eUICC.
+ * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+ * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+ *
+ * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+ * support eUICC.
+ *
* @hide
*/
- //TODO(b/77914569) Changes this to System Api.
+ @SystemApi
public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
/**
+ * List of ISO country codes in which eUICC UI is not shown. Country codes should be
+ * separated by comma.
+ *
+ * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+ * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+ *
+ * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+ * support eUICC.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
+
+ /**
* Whether any activity can be resized. When this is true, any
* activity, regardless of manifest values, can be resized for multi-window.
* (0 = false, 1 = true)
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9e454e6..22f90f6 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -44,7 +44,6 @@
import android.text.TextUtils;
import android.util.Patterns;
-import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.SmsApplication;
import java.lang.annotation.Retention;
@@ -1379,7 +1378,7 @@
}
String format = intent.getStringExtra("format");
- int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+ int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.getDefaultSmsSubscriptionId());
Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
@@ -4505,7 +4504,7 @@
/**
* The current registered voice network operator name in long alphanumeric format.
* <p>
- * This is the same as {@link ServiceState#getVoiceOperatorAlphaLong()}.
+ * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
* @hide
*/
public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
@@ -4516,12 +4515,11 @@
* In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
* network operator name in long alphanumeric format.
* <p>
- * This is the same as {@link ServiceState#getVoiceOperatorAlphaShort()}.
+ * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
* @hide
*/
public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
-
/**
* The current registered operator numeric id.
* <p>
@@ -4535,7 +4533,7 @@
/**
* The current registered data network operator name in long alphanumeric format.
* <p>
- * This is the same as {@link ServiceState#getDataOperatorAlphaLong()}.
+ * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
* @hide
*/
public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
@@ -4543,7 +4541,7 @@
/**
* The current registered data network operator name in short alphanumeric format.
* <p>
- * This is the same as {@link ServiceState#getDataOperatorAlphaShort()}.
+ * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
* @hide
*/
public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
@@ -4551,7 +4549,7 @@
/**
* The current registered data network operator numeric id.
* <p>
- * This is the same as {@link ServiceState#getDataOperatorNumeric()}.
+ * This is the same as {@link ServiceState#getOperatorNumeric()}.
* @hide
*/
public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 6a29d48..5d00370 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -242,6 +242,7 @@
synchronized (mLock) {
if (mDestroyed) return;
if (mUpdateCalled) {
+ mFillView.setOnClickListener(null);
hide();
mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/BooleanAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/BooleanAction.aidl
index 007ec94..730ad36 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/BooleanAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable BooleanAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/BooleanAction.java b/core/java/android/service/controls/BooleanAction.java
new file mode 100644
index 0000000..8508c63
--- /dev/null
+++ b/core/java/android/service/controls/BooleanAction.java
@@ -0,0 +1,93 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link ToggleTemplate}
+ * @hide
+ */
+public final class BooleanAction extends ControlAction {
+
+ private final boolean mNewState;
+
+ /**
+ * @param templateId the identifier of the {@link ToggleTemplate} that produced this action.
+ * @param newState new value for the state displayed by the {@link ToggleTemplate}.
+ */
+ public BooleanAction(@NonNull String templateId, boolean newState) {
+ this(templateId, newState, null);
+ }
+
+ /**
+ * @param templateId the identifier of the {@link ToggleTemplate} that originated this action.
+ * @param newValue new value for the state displayed by the {@link ToggleTemplate}.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
+ public BooleanAction(@NonNull String templateId, boolean newValue,
+ @Nullable String challengeValue) {
+ super(templateId, challengeValue);
+ mNewState = newValue;
+ }
+
+ BooleanAction(Parcel in) {
+ super(in);
+ mNewState = in.readByte() == 1;
+ }
+
+ /**
+ * The new state set for the button in the corresponding {@link ToggleTemplate}.
+ *
+ * @return {@code true} if the button was toggled from an {@code off} state to an {@code on}
+ * state.
+ */
+ public boolean getNewState() {
+ return mNewState;
+ }
+
+ /**
+ * @return {@link ControlAction#TYPE_BOOLEAN}
+ */
+ @Override
+ public int getActionType() {
+ return ControlAction.TYPE_BOOLEAN;
+ }
+
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeByte(mNewState ? (byte) 1 : (byte) 0);
+ }
+
+ public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
+ @Override
+ public BooleanAction createFromParcel(Parcel source) {
+ return new BooleanAction(source);
+ }
+
+ @Override
+ public BooleanAction[] newArray(int size) {
+ return new BooleanAction[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/Control.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/Control.aidl
index 007ec94..f4964f2 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/Control.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable Control;
\ No newline at end of file
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
new file mode 100644
index 0000000..a69408c
--- /dev/null
+++ b/core/java/android/service/controls/Control.java
@@ -0,0 +1,284 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents a physical object that can be represented by a {@link ControlTemplate} and whose
+ * properties may be modified through a {@link ControlAction}.
+ *
+ * The information is provided by a {@link ControlProviderService} and represents static
+ * information (not current status) about the device.
+ * <p>
+ * Each control needs a unique (per provider) identifier that is persistent across reboots of the
+ * system.
+ * <p>
+ * Each {@link Control} will have a name and an icon. The name is usually set up by the user in the
+ * {@link ControlProvider} while the icon is usually decided by the {@link ControlProvider} based
+ * on the type of device.
+ * <p>
+ * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
+ * non-interactive situations (for example when there's no state to display). This template is not
+ * the one that will be shown with the current state and provide interactions. That template is set
+ * using {@link ControlState}.
+ * <p>
+ * An {@link Intent} linking to the provider Activity that expands this {@link Control} should be
+ * provided.
+ * @hide
+ */
+public class Control implements Parcelable {
+
+ private final @NonNull String mControlId;
+ private final @NonNull Icon mIcon;
+ private final @NonNull CharSequence mTitle;
+ private final @Nullable ColorStateList mTintColor;
+ private final @NonNull Intent mAppIntent;
+ private final @ControlTemplate.TemplateType int mPrimaryType;
+
+ /**
+ * @param controlId the unique persistent identifier for this object.
+ * @param icon an icon to display identifying the control.
+ * @param title the user facing name of this control (e.g. "Bedroom thermostat").
+ * @param tintColor the color to tint parts of the element UI. If {@code null} is passed, the
+ * system accent color will be used.
+ * @param appIntent an intent linking to a page to interact with the corresponding device.
+ * @param primaryType the primary template for this type.
+ */
+ public Control(@NonNull String controlId,
+ @NonNull Icon icon,
+ @NonNull CharSequence title,
+ @Nullable ColorStateList tintColor,
+ @NonNull Intent appIntent,
+ int primaryType) {
+ Preconditions.checkNotNull(controlId);
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(title);
+ Preconditions.checkNotNull(appIntent);
+ mControlId = controlId;
+ mIcon = icon;
+ mTitle = title;
+ mTintColor = tintColor;
+ mAppIntent = appIntent;
+ mPrimaryType = primaryType;
+ }
+
+ public Control(Parcel in) {
+ mControlId = in.readString();
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ mTitle = in.readCharSequence();
+ if (in.readByte() == 1) {
+ mTintColor = ColorStateList.CREATOR.createFromParcel(in);
+ } else {
+ mTintColor = null;
+ }
+ mAppIntent = Intent.CREATOR.createFromParcel(in);
+ mPrimaryType = in.readInt();
+ }
+
+ @NonNull
+ public String getControlId() {
+ return mControlId;
+ }
+
+ @NonNull
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ @NonNull
+ public CharSequence getTitle() {
+ return mTitle;
+ }
+
+ @Nullable
+ public ColorStateList getTint() {
+ return mTintColor;
+ }
+
+ @NonNull
+ public Intent getAppIntent() {
+ return mAppIntent;
+ }
+
+ @ControlTemplate.TemplateType
+ public int getPrimaryType() {
+ return mPrimaryType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mControlId);
+ mIcon.writeToParcel(dest, flags);
+ dest.writeCharSequence(mTitle);
+ if (mTintColor != null) {
+ dest.writeByte((byte) 1);
+ mTintColor.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ mAppIntent.writeToParcel(dest, flags);
+ dest.writeInt(mPrimaryType);
+ }
+
+ public static final Creator<Control> CREATOR = new Creator<Control>() {
+ @Override
+ public Control createFromParcel(Parcel source) {
+ return new Control(source);
+ }
+
+ @Override
+ public Control[] newArray(int size) {
+ return new Control[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link Control}.
+ *
+ * This class facilitates the creation of {@link Control}. It provides the following
+ * defaults for non-optional parameters:
+ * <ul>
+ * <li> Title: {@code ""}
+ * <li> Primary template: {@link ControlTemplate#TYPE_NONE}
+ * </ul>
+ */
+ public static class Builder {
+ private String mControlId;
+ private Icon mIcon;
+ private CharSequence mTitle = "";
+ private ColorStateList mTintColor;
+ private @Nullable Intent mAppIntent;
+ private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @param icon the icon for the {@link Control}.
+ * @param appIntent the intent linking to the device Activity.
+ */
+ public Builder(@NonNull String controlId,
+ @NonNull Icon icon,
+ @NonNull Intent appIntent) {
+ Preconditions.checkNotNull(controlId);
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(appIntent);
+ mControlId = controlId;
+ mIcon = icon;
+ mAppIntent = appIntent;
+ }
+
+ /**
+ * Creates a {@link Builder} using an existing {@link Control} as a base.
+ * @param control base for the builder.
+ */
+ public Builder(@NonNull Control control) {
+ Preconditions.checkNotNull(control);
+ mControlId = control.mControlId;
+ mIcon = control.mIcon;
+ mTitle = control.mTitle;
+ mTintColor = control.mTintColor;
+ mAppIntent = control.mAppIntent;
+ mPrimaryType = control.mPrimaryType;
+ }
+
+ /**
+ * @param controlId the identifier for the {@link Control}.
+ * @return {@code this}
+ */
+ public Builder setControlId(@NonNull String controlId) {
+ Preconditions.checkNotNull(controlId);
+ mControlId = controlId;
+ return this;
+ }
+
+ /**
+ * @param icon the icon for the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setIcon(@NonNull Icon icon) {
+ Preconditions.checkNotNull(icon);
+ mIcon = icon;
+ return this;
+ }
+
+ /**
+ * @param title the user facing name of the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setTitle(@NonNull CharSequence title) {
+ Preconditions.checkNotNull(title);
+ mTitle = title;
+ return this;
+ }
+
+ /**
+ * @param tint colors for tinting parts of the {@link Control} UI. Passing {@code null} will
+ * default to using the current color accent.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setTint(@Nullable ColorStateList tint) {
+ mTintColor = tint;
+ return this;
+ }
+
+ /**
+ * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setAppIntent(@NonNull Intent appIntent) {
+ Preconditions.checkNotNull(appIntent);
+ mAppIntent = appIntent;
+ return this;
+ }
+
+ /**
+ * @param type type to use as default in the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
+ mPrimaryType = type;
+ return this;
+ }
+
+ /**
+ * Build a {@link Control}
+ * @return a valid {@link Control}
+ */
+ @NonNull
+ public Control build() {
+ return new Control(mControlId, mIcon, mTitle, mTintColor, mAppIntent, mPrimaryType);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlAction.aidl
index 007ec94..e1a5276 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ControlAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlAction.java b/core/java/android/service/controls/ControlAction.java
new file mode 100644
index 0000000..8b75955
--- /dev/null
+++ b/core/java/android/service/controls/ControlAction.java
@@ -0,0 +1,189 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract action that is executed from a {@link ControlTemplate}.
+ *
+ * The action may have a value to authenticate the input, when the provider has requested it to
+ * complete the action.
+ * @hide
+ */
+public abstract class ControlAction implements Parcelable {
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_BOOLEAN,
+ TYPE_FLOAT
+ })
+ public @interface ActionType {};
+
+ /**
+ * The identifier of {@link BooleanAction}.
+ */
+ public static final @ActionType int TYPE_BOOLEAN = 0;
+
+ /**
+ * The identifier of {@link FloatAction}.
+ */
+ public static final @ActionType int TYPE_FLOAT = 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ RESPONSE_OK,
+ RESPONSE_FAIL,
+ RESPONSE_CHALLENGE_ACK,
+ RESPONSE_CHALLENGE_PIN,
+ RESPONSE_CHALLENGE_PASSPHRASE
+ })
+ public @interface ResponseResult {};
+
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * the action has been performed. The action may still fail later and the state may not change.
+ */
+ public static final @ResponseResult int RESPONSE_OK = 0;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * the action has failed.
+ */
+ public static final @ResponseResult int RESPONSE_FAIL = 1;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, acknowledgment from the user is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, a PIN is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
+ /**
+ * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+ * in order for the action to be performed, an alphanumeric passphrase is required.
+ */
+ public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
+
+ /**
+ * The {@link ActionType} associated with this class.
+ */
+ public abstract @ActionType int getActionType();
+
+ private final @NonNull String mTemplateId;
+ private final @Nullable String mChallengeValue;
+
+ private ControlAction() {
+ mTemplateId = "";
+ mChallengeValue = null;
+ }
+
+ /**
+ * @hide
+ */
+ ControlAction(@NonNull String templateId, @Nullable String challengeValue) {
+ Preconditions.checkNotNull(templateId);
+ mTemplateId = templateId;
+ mChallengeValue = challengeValue;
+ }
+
+ /**
+ * @hide
+ */
+ ControlAction(Parcel in) {
+ mTemplateId = in.readString();
+ if (in.readByte() == 1) {
+ mChallengeValue = in.readString();
+ } else {
+ mChallengeValue = null;
+ }
+ }
+
+ /**
+ * The identifier of the {@link ControlTemplate} that originated this action
+ */
+ @NonNull
+ public String getTemplateId() {
+ return mTemplateId;
+ }
+
+ /**
+ * The challenge value used to authenticate certain actions, if available.
+ */
+ @Nullable
+ public String getChallengeValue() {
+ return mChallengeValue;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getActionType());
+ dest.writeString(mTemplateId);
+ if (mChallengeValue != null) {
+ dest.writeByte((byte) 1);
+ dest.writeString(mChallengeValue);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ }
+
+ public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
+ @Override
+ public ControlAction createFromParcel(Parcel source) {
+ int type = source.readInt();
+ return createActionFromType(type, source);
+ }
+
+ @Override
+ public ControlAction[] newArray(int size) {
+ return new ControlAction[size];
+ }
+ };
+
+ private static ControlAction createActionFromType(@ActionType int type, Parcel source) {
+ switch(type) {
+ case TYPE_BOOLEAN:
+ return BooleanAction.CREATOR.createFromParcel(source);
+ case TYPE_FLOAT:
+ return FloatAction.CREATOR.createFromParcel(source);
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlButton.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlButton.aidl
index 007ec94..6a7262d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlButton.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ControlButton;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlButton.java b/core/java/android/service/controls/ControlButton.java
new file mode 100644
index 0000000..fed3115
--- /dev/null
+++ b/core/java/android/service/controls/ControlButton.java
@@ -0,0 +1,103 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Button element for {@link ControlTemplate}.
+ * @hide
+ */
+public class ControlButton implements Parcelable {
+
+ private final boolean mActive;
+ private final @NonNull Icon mIcon;
+ private final @NonNull CharSequence mContentDescription;
+
+ /**
+ * @param active true if the button should be rendered as active.
+ * @param icon icon to display in the button.
+ * @param contentDescription content description for the button.
+ */
+ public ControlButton(boolean active, @NonNull Icon icon,
+ @NonNull CharSequence contentDescription) {
+ Preconditions.checkNotNull(icon);
+ Preconditions.checkNotNull(contentDescription);
+ mActive = active;
+ mIcon = icon;
+ mContentDescription = contentDescription;
+ }
+
+ /**
+ * Whether the button should be rendered in its active state.
+ */
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * The icon for this button.
+ */
+ @NonNull
+ public Icon getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * The content description for this button.
+ */
+ @NonNull
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte(mActive ? (byte) 1 : (byte) 0);
+ mIcon.writeToParcel(dest, flags);
+ dest.writeCharSequence(mContentDescription);
+ }
+
+ ControlButton(Parcel in) {
+ mActive = in.readByte() != 0;
+ mIcon = Icon.CREATOR.createFromParcel(in);
+ mContentDescription = in.readCharSequence();
+ }
+
+ public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
+ @Override
+ public ControlButton createFromParcel(Parcel source) {
+ return new ControlButton(source);
+ }
+
+ @Override
+ public ControlButton[] newArray(int size) {
+ return new ControlButton[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlState.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlState.aidl
index 007ec94..520d85b 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlState.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ControlState;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
new file mode 100644
index 0000000..804aef7
--- /dev/null
+++ b/core/java/android/service/controls/ControlState.java
@@ -0,0 +1,318 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Current state for a {@link Control}.
+ *
+ * Collects information to render the current state of a {@link Control} as well as possible action
+ * that can be performed on it. Some of the information may temporarily override the defaults
+ * provided by the corresponding {@link Control}, while this state is being displayed.
+ *
+ * Additionally, this can be used to modify information related to the corresponding
+ * {@link Control}.
+ * @hide
+ */
+public final class ControlState implements Parcelable {
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STATUS_OK,
+ STATUS_NOT_FOUND,
+ STATUS_ERROR,
+ STATUS_DISABLED,
+ })
+ public @interface Status {};
+
+ /**
+ * The device corresponding to the {@link Control} is responding correctly.
+ */
+ public static final int STATUS_OK = 0;
+
+ /**
+ * The device corresponding to the {@link Control} cannot be found or was removed.
+ */
+ public static final int STATUS_NOT_FOUND = 1;
+
+ /**
+ * The device corresponding to the {@link Control} is in an error state.
+ */
+ public static final int STATUS_ERROR = 2;
+
+ /**
+ * The {@link Control} is currently disabled.
+ */
+ public static final int STATUS_DISABLED = 3;
+
+ private final @NonNull Control mControl;
+ private final @Status int mStatus;
+ private final @NonNull ControlTemplate mControlTemplate;
+ private final @NonNull CharSequence mStatusText;
+ private final @Nullable Icon mOverrideIcon;
+ private final @Nullable ColorStateList mOverrideTint;
+
+ /**
+ * @param control the {@link Control} this state should be applied to. Can be used to
+ * update information about the {@link Control}
+ * @param status the current status of the {@link Control}.
+ * @param controlTemplate the template to be used to render the {@link Control}.
+ * @param statusText the text describing the current status.
+ * @param overrideIcon the icon to temporarily override the one provided in
+ * {@link Control#getIcon()}. Pass {@code null} to use the icon in
+ * {@link Control#getIcon()}.
+ * @param overrideTint the colors to temporarily override those provided in
+ * {@link Control#getTint()}. Pass {@code null} to use the colors in
+ * {@link Control#getTint()}.
+ */
+ public ControlState(@NonNull Control control,
+ int status,
+ @NonNull ControlTemplate controlTemplate,
+ @NonNull CharSequence statusText,
+ @Nullable Icon overrideIcon,
+ @Nullable ColorStateList overrideTint) {
+ Preconditions.checkNotNull(control);
+ Preconditions.checkNotNull(controlTemplate);
+ Preconditions.checkNotNull(statusText);
+
+ mControl = control;
+ mStatus = status;
+ mControlTemplate = controlTemplate;
+ mOverrideIcon = overrideIcon;
+ mStatusText = statusText;
+ mOverrideTint = overrideTint;
+ }
+
+ ControlState(Parcel in) {
+ mControl = Control.CREATOR.createFromParcel(in);
+ mStatus = in.readInt();
+ mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+ mStatusText = in.readCharSequence();
+ if (in.readByte() == 1) {
+ mOverrideIcon = Icon.CREATOR.createFromParcel(in);
+ } else {
+ mOverrideIcon = null;
+ }
+ if (in.readByte() == 1) {
+ mOverrideTint = ColorStateList.CREATOR.createFromParcel(in);
+ } else {
+ mOverrideTint = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Status
+ public int getStatus() {
+ return mStatus;
+ }
+
+ @NonNull
+ public ControlTemplate getControlTemplate() {
+ return mControlTemplate;
+ }
+
+ @Nullable
+ public Icon getOverrideIcon() {
+ return mOverrideIcon;
+ }
+
+ @NonNull
+ public CharSequence getStatusText() {
+ return mStatusText;
+ }
+
+ @Nullable
+ public ColorStateList getOverrideTint() {
+ return mOverrideTint;
+ }
+
+ @NonNull
+ public Control getControl() {
+ return mControl;
+ }
+
+ @NonNull
+ public String getControlId() {
+ return mControl.getControlId();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mControl.writeToParcel(dest, flags);
+ dest.writeInt(mStatus);
+ mControlTemplate.writeToParcel(dest, flags);
+ dest.writeCharSequence(mStatusText);
+ if (mOverrideIcon != null) {
+ dest.writeByte((byte) 1);
+ mOverrideIcon.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ if (mOverrideTint != null) {
+ dest.writeByte((byte) 1);
+ mOverrideTint.writeToParcel(dest, flags);
+ } else {
+ dest.writeByte((byte) 0);
+ }
+ }
+
+ public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
+ @Override
+ public ControlState createFromParcel(Parcel source) {
+ return new ControlState(source);
+ }
+
+ @Override
+ public ControlState[] newArray(int size) {
+ return new ControlState[size];
+ }
+ };
+
+ /**
+ * Builder class for {@link ControlState}.
+ *
+ * This class facilitates the creation of {@link ControlState}. It provides the following
+ * defaults for non-optional parameters:
+ * <ul>
+ * <li> Status: {@link ControlState#STATUS_OK}
+ * <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+ * <li> Status text: {@code ""}
+ * </ul>
+ */
+ public static class Builder {
+ private @NonNull Control mControl;
+ private @Status int mStatus = STATUS_OK;
+ private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+ private @NonNull CharSequence mStatusText = "";
+ private @Nullable Icon mOverrideIcon;
+ private @Nullable ColorStateList mOverrideTint;
+
+ /**
+ * @param control the {@link Control} that the resulting {@link ControlState} refers to.
+ */
+ public Builder(@NonNull Control control) {
+ Preconditions.checkNotNull(control);
+ mControl = control;
+ }
+
+ /**
+ * Creates a {@link Builder} using an existing {@link ControlState} as a base.
+ * @param controlState base for the builder.
+ */
+ public Builder(@NonNull ControlState controlState) {
+ Preconditions.checkNotNull(controlState);
+ mControl = controlState.mControl;
+ mControlTemplate = controlState.mControlTemplate;
+ mOverrideIcon = controlState.mOverrideIcon;
+ mStatusText = controlState.mStatusText;
+ mOverrideTint = controlState.mOverrideTint;
+ }
+
+
+ /**
+ * @param control the updated {@link Control} information.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setControl(@NonNull Control control) {
+ mControl = control;
+ return this;
+ }
+
+ /**
+ * @param status the current status of the {@link Control}
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setStatus(@Status int status) {
+ mStatus = status;
+ return this;
+ }
+
+ /**
+ * @param controlTemplate the template to use when rendering the {@code Control}.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
+ Preconditions.checkNotNull(controlTemplate);
+ mControlTemplate = controlTemplate;
+ return this;
+ }
+
+ /**
+ * @param statusText the user-visible description of the status.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setStatusText(@NonNull CharSequence statusText) {
+ Preconditions.checkNotNull(statusText);
+ mStatusText = statusText;
+ return this;
+ }
+
+ /**
+ * @param overrideIcon the icon to override the one defined in the corresponding
+ * {@code Control}. Pass {@code null} to remove the override.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setOverrideIcon(@Nullable Icon overrideIcon) {
+ mOverrideIcon = overrideIcon;
+ return this;
+ }
+
+ /**
+ * @param overrideTint the colors to override the ones defined in the corresponding
+ * {@code Control}. Pass {@code null} to remove the override.
+ * @return {@code this}
+ */
+ @NonNull
+ public Builder setOverrideTint(@Nullable ColorStateList overrideTint) {
+ mOverrideTint = overrideTint;
+ return this;
+ }
+
+ /**
+ * @return a new {@link ControlState}
+ */
+ public ControlState build() {
+ return new ControlState(mControl, mStatus, mControlTemplate, mStatusText,
+ mOverrideIcon, mOverrideTint);
+ }
+ }
+}
+
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlTemplate.aidl
index 007ec94..ecb948c 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ControlTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlTemplate.java b/core/java/android/service/controls/ControlTemplate.java
new file mode 100644
index 0000000..e559862
--- /dev/null
+++ b/core/java/android/service/controls/ControlTemplate.java
@@ -0,0 +1,167 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract input template for a {@link Control}.
+ *
+ * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
+ * particular {@link Control}.
+ * <p>
+ * Some instances of {@link Control} can originate actions (via user interaction) to modify its
+ * associated state. The actions available to a given {@link Control} in a particular
+ * {@link ControlState} are determined by its {@link ControlTemplate}.
+ * @see ControlAction
+ * @hide
+ */
+public abstract class ControlTemplate implements Parcelable {
+
+ /**
+ * Singleton representing a {@link Control} with no input.
+ */
+ public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
+ @Override
+ public int getTemplateType() {
+ return TYPE_NONE;
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_NONE,
+ TYPE_TOGGLE,
+ TYPE_RANGE,
+ TYPE_THUMBNAIL,
+ TYPE_DISCRETE_TOGGLE,
+ TYPE_COORD_RANGE
+ })
+ public @interface TemplateType {}
+
+ /**
+ * Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
+ */
+ public static final int TYPE_NONE = 0;
+
+ /**
+ * Type identifier of {@link ToggleTemplate}.
+ */
+ public static final int TYPE_TOGGLE = 1;
+
+ /**
+ * Type identifier of {@link RangeTemplate}.
+ */
+ public static final int TYPE_RANGE = 2;
+
+ /**
+ * Type identifier of {@link ThumbnailTemplate}.
+ */
+ public static final int TYPE_THUMBNAIL = 3;
+
+ /**
+ * Type identifier of {@link DiscreteToggleTemplate}.
+ */
+ public static final int TYPE_DISCRETE_TOGGLE = 4;
+
+ /**
+ * @hide
+ */
+ public static final int TYPE_COORD_RANGE = 5;
+
+ private @NonNull final String mTemplateId;
+
+ /**
+ * @return the identifier for this object.
+ */
+ public String getTemplateId() {
+ return mTemplateId;
+ }
+
+ /**
+ * The {@link TemplateType} associated with this class.
+ */
+ public abstract @TemplateType int getTemplateType();
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getTemplateType());
+ dest.writeString(mTemplateId);
+ }
+
+ private ControlTemplate() {
+ mTemplateId = "";
+ }
+
+ ControlTemplate(Parcel in) {
+ mTemplateId = in.readString();
+ }
+
+ /**
+ * @hide
+ */
+ ControlTemplate(@NonNull String templateId) {
+ Preconditions.checkNotNull(templateId);
+ mTemplateId = templateId;
+ }
+
+ public static final Creator<ControlTemplate> CREATOR = new Creator<ControlTemplate>() {
+ @Override
+ public ControlTemplate createFromParcel(Parcel source) {
+ int type = source.readInt();
+ return createTemplateFromType(type, source);
+ }
+
+ @Override
+ public ControlTemplate[] newArray(int size) {
+ return new ControlTemplate[size];
+ }
+ };
+
+ private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) {
+ switch(type) {
+ case TYPE_TOGGLE:
+ return ToggleTemplate.CREATOR.createFromParcel(source);
+ case TYPE_RANGE:
+ return RangeTemplate.CREATOR.createFromParcel(source);
+ case TYPE_THUMBNAIL:
+ return ThumbnailTemplate.CREATOR.createFromParcel(source);
+ case TYPE_DISCRETE_TOGGLE:
+ return DiscreteToggleTemplate.CREATOR.createFromParcel(source);
+ case TYPE_NONE:
+ return NO_TEMPLATE;
+ default:
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/service/controls/DiscreteToggleTemplate.java b/core/java/android/service/controls/DiscreteToggleTemplate.java
new file mode 100644
index 0000000..5167af4
--- /dev/null
+++ b/core/java/android/service/controls/DiscreteToggleTemplate.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with two discrete inputs.
+ *
+ * The two inputs represent a <i>Negative</i> input and a <i>Positive</i> input.
+ * <p>
+ * When one of the buttons is actioned, a {@link BooleanAction} will be sent.
+ * {@link BooleanAction#getNewState} will be {@code false} if the button was
+ * {@link DiscreteToggleTemplate#getNegativeButton} and {@code true} if the button was
+ * {@link DiscreteToggleTemplate#getPositiveButton}.
+ * @hide
+ */
+public class DiscreteToggleTemplate extends ControlTemplate {
+
+ private final @NonNull ControlButton mNegativeButton;
+ private final @NonNull ControlButton mPositiveButton;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param negativeButton a {@ControlButton} for the <i>Negative</i> input
+ * @param positiveButton a {@ControlButton} for the <i>Positive</i> input
+ */
+ public DiscreteToggleTemplate(@NonNull String templateId,
+ @NonNull ControlButton negativeButton,
+ @NonNull ControlButton positiveButton) {
+ super(templateId);
+ Preconditions.checkNotNull(negativeButton);
+ Preconditions.checkNotNull(positiveButton);
+ mNegativeButton = negativeButton;
+ mPositiveButton = positiveButton;
+ }
+
+ DiscreteToggleTemplate(Parcel in) {
+ super(in);
+ this.mNegativeButton = ControlButton.CREATOR.createFromParcel(in);
+ this.mPositiveButton = ControlButton.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * The {@link ControlButton} associated with the <i>Negative</i> action.
+ */
+ @NonNull
+ public ControlButton getNegativeButton() {
+ return mNegativeButton;
+ }
+
+ /**
+ * The {@link ControlButton} associated with the <i>Positive</i> action.
+ */
+ @NonNull
+ public ControlButton getPositiveButton() {
+ return mPositiveButton;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_DISCRETE_TOGGLE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_DISCRETE_TOGGLE;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mNegativeButton.writeToParcel(dest, flags);
+ mPositiveButton.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<DiscreteToggleTemplate> CREATOR =
+ new Creator<DiscreteToggleTemplate>() {
+ @Override
+ public DiscreteToggleTemplate createFromParcel(Parcel source) {
+ return new DiscreteToggleTemplate(source);
+ }
+
+ @Override
+ public DiscreteToggleTemplate[] newArray(int size) {
+ return new DiscreteToggleTemplate[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/FloatAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/FloatAction.aidl
index 007ec94..dbc0f72 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/FloatAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable FloatAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/FloatAction.java b/core/java/android/service/controls/FloatAction.java
new file mode 100644
index 0000000..fe6db10
--- /dev/null
+++ b/core/java/android/service/controls/FloatAction.java
@@ -0,0 +1,90 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link RangeTemplate}.
+ * @hide
+ */
+public final class FloatAction extends ControlAction {
+
+ private final float mNewValue;
+
+ /**
+ * @param templateId the identifier of the {@link RangeTemplate} that produced this action.
+ * @param newValue new value for the state displayed by the {@link RangeTemplate}.
+ */
+ public FloatAction(@NonNull String templateId, float newValue) {
+ this(templateId, newValue, null);
+ }
+
+ /**
+ * @param templateId the identifier of the {@link RangeTemplate} that originated this action.
+ * @param newValue new value for the state of the {@link RangeTemplate}.
+ * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+ * null is sent when no authentication is needed or has not been
+ * requested.
+ */
+
+ public FloatAction(@NonNull String templateId, float newValue,
+ @Nullable String challengeValue) {
+ super(templateId, challengeValue);
+ mNewValue = newValue;
+ }
+
+ public FloatAction(Parcel in) {
+ super(in);
+ mNewValue = in.readFloat();
+ }
+
+ /**
+ * The new value set for the range in the corresponding {@link RangeTemplate}.
+ */
+ public float getNewValue() {
+ return mNewValue;
+ }
+
+ /**
+ * @return {@link ControlAction#TYPE_FLOAT}
+ */
+ @Override
+ public int getActionType() {
+ return TYPE_FLOAT;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(mNewValue);
+ }
+
+ public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
+ @Override
+ public FloatAction createFromParcel(Parcel source) {
+ return new FloatAction(source);
+ }
+
+ @Override
+ public FloatAction[] newArray(int size) {
+ return new FloatAction[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/IControlsProvider.aidl
similarity index 61%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/IControlsProvider.aidl
index 007ec94..f778653 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,17 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+import android.service.controls.ControlAction;
+
+/** @hide */
+oneway interface IControlsProvider {
+ void load();
+
+ void subscribe(in List<String> controlIds);
+
+ void unsubscribe();
+
+ void onAction(in String controlId, in ControlAction action);
+}
\ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl
new file mode 100644
index 0000000..3dbb68c
--- /dev/null
+++ b/core/java/android/service/controls/IControlsProviderCallback.aidl
@@ -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.service.controls;
+
+import android.service.controls.Control;
+import android.service.controls.ControlState;
+
+/** @hide */
+oneway interface IControlsProviderCallback {
+ void onLoad(in List<Control> controls);
+
+ void onRefreshState(in List<ControlState> controlStates);
+
+ void onControlActionResponse(in String controlId, int response);
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/RangeTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/RangeTemplate.aidl
index 007ec94..a3d1ca0 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/RangeTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable RangeTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/RangeTemplate.java b/core/java/android/service/controls/RangeTemplate.java
new file mode 100644
index 0000000..70bf2dd
--- /dev/null
+++ b/core/java/android/service/controls/RangeTemplate.java
@@ -0,0 +1,190 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A template for a {@link Control} with inputs in a "continuous" range of values.
+ *
+ * @see FloatAction
+ * @hide
+ */
+public final class RangeTemplate extends ControlTemplate {
+
+ private final float mMinValue;
+ private final float mMaxValue;
+ private final float mCurrentValue;
+ private final float mStepValue;
+ private final @NonNull CharSequence mFormatString;
+
+ /**
+ * Construct a new {@link RangeTemplate}.
+ *
+ * The range must be valid, meaning:
+ * <ul>
+ * <li> {@code minValue} < {@code maxValue}
+ * <li> {@code minValue} < {@code currentValue}
+ * <li> {@code currentValue} < {@code maxValue}
+ * <li> 0 < {@code stepValue}
+ * </ul>
+ * <p>
+ * The current value of the Control will be formatted accordingly.
+ *
+ * @param templateId the identifier for this template object
+ * @param minValue minimum value for the input
+ * @param maxValue maximum value for the input
+ * @param currentValue the current value of the {@link ControlState} containing this object.
+ * @param stepValue minimum value of increments/decrements when interacting with this control.
+ * @param formatString a formatting string as per {@link String#format} used to display the
+ * {@code currentValue}. If {@code null} is passed, the "%.1f" is used.
+ * @throws InvalidParameterException if the parameters passed do not make a valid range.
+ */
+ public RangeTemplate(@NonNull String templateId,
+ float minValue,
+ float maxValue,
+ float currentValue,
+ float stepValue,
+ @Nullable CharSequence formatString) {
+ super(templateId);
+ Preconditions.checkNotNull(formatString);
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ mCurrentValue = currentValue;
+ mStepValue = stepValue;
+ if (formatString != null) {
+ mFormatString = formatString;
+ } else {
+ mFormatString = "%.1f";
+ }
+ validate();
+ }
+
+ /**
+ * Construct a new {@link RangeTemplate} from a {@link Parcel}.
+ *
+ * @throws InvalidParameterException if the parameters passed do not make a valid range
+ * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
+ * @hide
+ */
+ RangeTemplate(Parcel in) {
+ super(in);
+ mMinValue = in.readFloat();
+ mMaxValue = in.readFloat();
+ mCurrentValue = in.readFloat();
+ mStepValue = in.readFloat();
+ mFormatString = in.readCharSequence();
+ validate();
+ }
+
+ /**
+ * The minimum value for this range.
+ */
+ public float getMinValue() {
+ return mMinValue;
+ }
+
+ /**
+ * The maximum value for this range.
+ */
+ public float getMaxValue() {
+ return mMaxValue;
+ }
+
+ /**
+ * The current value for this range.
+ */
+ public float getCurrentValue() {
+ return mCurrentValue;
+ }
+
+ /**
+ * The value of the smallest increment or decrement that can be performed on this range.
+ */
+ public float getStepValue() {
+ return mStepValue;
+ }
+
+ /**
+ * Formatter for generating a user visible {@link String} representing the value
+ * returned by {@link RangeTemplate#getCurrentValue}.
+ * @return a formatting string as specified in {@link String#format}
+ */
+ @NonNull
+ public CharSequence getFormatString() {
+ return mFormatString;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_RANGE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_RANGE;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeFloat(mMinValue);
+ dest.writeFloat(mMaxValue);
+ dest.writeFloat(mCurrentValue);
+ dest.writeFloat(mStepValue);
+ dest.writeCharSequence(mFormatString);
+ }
+
+ /**
+ * Validate constructor parameters
+ *
+ * @throws InvalidParameterException if the parameters passed do not make a valid range
+ */
+ private void validate() {
+ if (Float.compare(mMinValue, mMaxValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("minValue=%f > maxValue=%f", mMinValue, mMaxValue));
+ }
+ if (Float.compare(mMinValue, mCurrentValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("minValue=%f > currentValue=%f", mMinValue, mCurrentValue));
+ }
+ if (Float.compare(mCurrentValue, mMaxValue) > 0) {
+ throw new InvalidParameterException(
+ String.format("currentValue=%f > maxValue=%f", mCurrentValue, mMaxValue));
+ }
+ if (mStepValue <= 0) {
+ throw new InvalidParameterException(String.format("stepValue=%f <= 0", mStepValue));
+ }
+ }
+
+ public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
+ @Override
+ public RangeTemplate createFromParcel(Parcel source) {
+ return new RangeTemplate(source);
+ }
+
+ @Override
+ public RangeTemplate[] newArray(int size) {
+ return new RangeTemplate[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ThumbnailTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ThumbnailTemplate.aidl
index 007ec94..fe8c7fe 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ThumbnailTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ThumbnailTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ThumbnailTemplate.java b/core/java/android/service/controls/ThumbnailTemplate.java
new file mode 100644
index 0000000..796d2de
--- /dev/null
+++ b/core/java/android/service/controls/ThumbnailTemplate.java
@@ -0,0 +1,95 @@
+/*
+ * 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.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} that displays an image.
+ * @hide
+ */
+public final class ThumbnailTemplate extends ControlTemplate {
+
+ private final @NonNull Icon mThumbnail;
+ private final @NonNull CharSequence mContentDescription;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param thumbnail an image to display on the {@link Control}
+ * @param contentDescription a description of the image for accessibility.
+ */
+ public ThumbnailTemplate(@NonNull String templateId, @NonNull Icon thumbnail,
+ @NonNull CharSequence contentDescription) {
+ super(templateId);
+ Preconditions.checkNotNull(thumbnail);
+ Preconditions.checkNotNull(contentDescription);
+ mThumbnail = thumbnail;
+ mContentDescription = contentDescription;
+ }
+
+ ThumbnailTemplate(Parcel in) {
+ super(in);
+ mThumbnail = Icon.CREATOR.createFromParcel(in);
+ mContentDescription = in.readCharSequence();
+ }
+
+ /**
+ * The {@link Icon} (image) displayed by this template.
+ */
+ @NonNull
+ public Icon getThumbnail() {
+ return mThumbnail;
+ }
+
+ /**
+ * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()}
+ */
+ @NonNull
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_THUMBNAIL}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_THUMBNAIL;
+ }
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mThumbnail.writeToParcel(dest, flags);
+ dest.writeCharSequence(mContentDescription);
+ }
+
+ public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
+ @Override
+ public ThumbnailTemplate createFromParcel(Parcel source) {
+ return new ThumbnailTemplate(source);
+ }
+
+ @Override
+ public ThumbnailTemplate[] newArray(int size) {
+ return new ThumbnailTemplate[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ToggleTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ToggleTemplate.aidl
index 007ec94..1c823d9 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ToggleTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi;
+package android.service.controls;
-parcelable WifiActivityEnergyInfo;
+parcelable ToggleTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ToggleTemplate.java b/core/java/android/service/controls/ToggleTemplate.java
new file mode 100644
index 0000000..3766bd1
--- /dev/null
+++ b/core/java/android/service/controls/ToggleTemplate.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 android.service.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with a single button that can be toggled between two states.
+ *
+ * The states for the toggle correspond to the states in {@link ControlButton#isActive()}.
+ * An action on this template will originate a {@link BooleanAction} to change that state.
+ *
+ * @see BooleanAction
+ * @hide
+ */
+public final class ToggleTemplate extends ControlTemplate {
+
+ private final @NonNull ControlButton mButton;
+
+ /**
+ * @param templateId the identifier for this template object
+ * @param button a {@ControlButton} that can show the current state and toggle it
+ */
+ public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) {
+ super(templateId);
+ Preconditions.checkNotNull(button);
+ mButton = button;
+ }
+
+ ToggleTemplate(Parcel in) {
+ super(in);
+ mButton = ControlButton.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * The button provided to this object in {@link ToggleTemplate#ToggleTemplate}
+ */
+ @NonNull
+ public ControlButton getButton() {
+ return mButton;
+ }
+
+ /**
+ * @return {@link ControlTemplate#TYPE_TOGGLE}
+ */
+ @Override
+ public int getTemplateType() {
+ return TYPE_TOGGLE;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ mButton.writeToParcel(dest, flags);
+ }
+
+ public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
+ @Override
+ public ToggleTemplate createFromParcel(Parcel source) {
+ return new ToggleTemplate(source);
+ }
+
+ @Override
+ public ToggleTemplate[] newArray(int size) {
+ return new ToggleTemplate[size];
+ }
+ };
+
+}
diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl b/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl
deleted file mode 100644
index 723fc59..0000000
--- a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.service.incremental;
-
-import android.os.incremental.IncrementalDataLoaderParamsParcel;
-import android.os.incremental.IncrementalFileSystemControlParcel;
-import android.service.incremental.IIncrementalDataLoaderStatusListener;
-
-/** @hide */
-oneway interface IIncrementalDataLoaderService {
- void createDataLoader(in int storageId,
- in IncrementalFileSystemControlParcel control,
- in IncrementalDataLoaderParamsParcel params,
- in IIncrementalDataLoaderStatusListener listener,
- in boolean start);
- void startDataLoader(in int storageId);
- void stopDataLoader(in int storageId);
- void destroyDataLoader(in int storageId);
- void onFileCreated(in int storageId, in long inode, in byte[] metadata);
-}
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
index 3f3c6b8..dd3904f 100644
--- a/core/java/android/service/notification/IConditionProvider.aidl
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2014, The Android Open Source Project
+ * 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.
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
new file mode 100644
index 0000000..4474f3e
--- /dev/null
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -0,0 +1,96 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * A static helper class used to send Intents with prepopulated flags.
+ * <p>
+ * This is intended to be used by the CellBroadcastService and will throw a security exception if
+ * used from a UID besides the network stack UID.
+ *
+ * @hide
+ */
+@SystemApi
+public class CellBroadcastIntents {
+ private static final String LOG_TAG = "CellBroadcastIntents";
+
+ /**
+ * @hide
+ */
+ private CellBroadcastIntents() {
+ }
+
+ /**
+ * Returns an intent which can be received by background BroadcastReceivers. This is only
+ * intended to be used by the CellBroadcastService and will throw a security exception if called
+ * from another UID.
+ *
+ * @param context The context from which to send the broadcast
+ * @param user The user from which to send the broadcast
+ * @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 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.
+ */
+ public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull Context context,
+ @Nullable UserHandle user, @NonNull Intent intent, @Nullable String receiverPermission,
+ @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
+ @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+ @Nullable Bundle initialExtras) {
+ Log.d(LOG_TAG, "sendOrderedBroadcastForBackgroundReceivers intent=" + intent.getAction());
+ int status = context.checkCallingOrSelfPermission(
+ "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS");
+ if (status == PackageManager.PERMISSION_DENIED) {
+ throw new SecurityException(
+ "Caller does not have permission to send broadcast for background receivers");
+ }
+ intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ if (user != null) {
+ context.createContextAsUser(user, 0).sendOrderedBroadcast(intent, receiverPermission,
+ receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
+ initialExtras);
+ } else {
+ context.sendOrderedBroadcast(intent, receiverPermission,
+ receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
+ initialExtras);
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl b/core/java/android/telephony/DataConnectionRealTimeInfo.aidl
similarity index 100%
rename from telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl
rename to core/java/android/telephony/DataConnectionRealTimeInfo.aidl
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java
similarity index 100%
rename from telephony/java/android/telephony/DataConnectionRealTimeInfo.java
rename to core/java/android/telephony/DataConnectionRealTimeInfo.java
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index a65c8fd..716a522 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -286,14 +286,6 @@
public static final int LISTEN_USER_MOBILE_DATA_STATE = 0x00080000;
/**
- * Listen for changes to the physical channel configuration.
- *
- * @see #onPhysicalChannelConfigurationChanged
- * @hide
- */
- public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION = 0x00100000;
-
- /**
* Listen for changes to the phone capability.
*
* @see #onPhoneCapabilityChanged
@@ -831,24 +823,6 @@
}
/**
- * Callback invoked when the current physical channel configuration has changed on the
- * registered subscription.
- * Note, the registration subId comes from {@link TelephonyManager} object which registers
- * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
- * If this TelephonyManager object was created with
- * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
- * subId. Otherwise, this callback applies to
- * {@link SubscriptionManager#getDefaultSubscriptionId()}.
- *
- * @param configs List of the current {@link PhysicalChannelConfig}s
- * @hide
- */
- public void onPhysicalChannelConfigurationChanged(
- @NonNull List<PhysicalChannelConfig> configs) {
- // default implementation empty
- }
-
- /**
* Callback invoked when the current emergency number list has changed on the registered
* subscription.
* Note, the registration subId comes from {@link TelephonyManager} object which registers
@@ -1194,15 +1168,6 @@
() -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
}
- public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
- PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
- if (psl == null) return;
-
- Binder.withCleanCallingIdentity(
- () -> mExecutor.execute(
- () -> psl.onPhysicalChannelConfigurationChanged(configs)));
- }
-
public void onEmergencyNumberListChanged(Map emergencyNumberList) {
PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index f66a679..9d7b57b 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -455,6 +455,19 @@
}
/**
+ * Sim activation type: voice
+ * @see #notifyVoiceActivationStateChanged
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
+ /**
+ * Sim activation type: data
+ * @see #notifyDataActivationStateChanged
+ * @hide
+ */
+ public static final int SIM_ACTIVATION_TYPE_DATA = 1;
+
+ /**
* Notify data activation state changed on certain subscription.
* @see TelephonyManager#getDataActivationState()
*
@@ -469,7 +482,7 @@
@SimActivationState int activationState) {
try {
sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
- TelephonyManager.SIM_ACTIVATION_TYPE_DATA, activationState);
+ SIM_ACTIVATION_TYPE_DATA, activationState);
} catch (RemoteException ex) {
// system process is dead
}
@@ -490,7 +503,7 @@
@SimActivationState int activationState) {
try {
sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
- TelephonyManager.SIM_ACTIVATION_TYPE_VOICE, activationState);
+ SIM_ACTIVATION_TYPE_VOICE, activationState);
} catch (RemoteException ex) {
// system process is dead
}
diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java
index e2d1596..0e4eec44 100644
--- a/core/java/android/text/LoginFilter.java
+++ b/core/java/android/text/LoginFilter.java
@@ -19,7 +19,10 @@
/**
* Abstract class for filtering login-related text (user names and passwords)
*
+ * @deprecated Password requirements should not be hardcoded in clients. This class also does not
+ * handle non-BMP characters.
*/
+@Deprecated
public abstract class LoginFilter implements InputFilter {
private boolean mAppendInvalid; // whether to append or ignore invalid characters
/**
@@ -130,7 +133,9 @@
* account creation. It prevents the user from entering user names with characters other than
* [a-zA-Z0-9.].
*
+ * @deprecated Do not encode assumptions about Google account names into client applications.
*/
+ @Deprecated
public static class UsernameFilterGMail extends LoginFilter {
public UsernameFilterGMail() {
@@ -190,8 +195,12 @@
/**
* This filter is compatible with GMail passwords which restricts characters to
* the Latin-1 (ISO8859-1) char set.
- *
+ *
+ * @deprecated Do not handle a user's Google password. Refer to
+ * <a href="https://support.google.com/accounts/answer/32040">Google Help</a> for
+ * password restriction information.
*/
+ @Deprecated
public static class PasswordFilterGMail extends LoginFilter {
public PasswordFilterGMail() {
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index d7ec30c..0a4069d 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -177,7 +177,7 @@
* @hide
**/
@VisibleForTesting
- public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x400;
+ public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
// Size limits.
@@ -628,9 +628,9 @@
if (0 == mErrorMask) {
mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
} else {
- mBuffer.putByte(0, TYPE_ERRORS);
- mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
+ mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
mPos += mBuffer.putInt(mPos, mErrorMask);
+ mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
size = mPos;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 3c93bb7..bc70d63 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -467,6 +467,10 @@
}
}
+ boolean isAnimating() {
+ return mAnimationDirection != DIRECTION_NONE;
+ }
+
private InsetsSourceConsumer createConsumerOfType(int type) {
if (type == ITYPE_IME) {
return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
@@ -514,6 +518,7 @@
} else {
hideDirectly(types);
}
+ mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
mAnimator = ObjectAnimator.ofObject(
controller,
new InsetsProperty(),
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index b1caf18..c6d9898 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -167,7 +167,8 @@
}
private void applyHiddenToControl() {
- if (mSourceControl == null || mSourceControl.getLeash() == null) {
+ if (mSourceControl == null || mSourceControl.getLeash() == null
+ || mController.isAnimating()) {
return;
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fc80e00..9e5e20a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -442,6 +442,12 @@
public static final int METADATA_TASK_ID = 3;
/**
+ * Accessibility ID to allow association between surfaces and accessibility tree.
+ * @hide
+ */
+ public static final int METADATA_ACCESSIBILITY_ID = 4;
+
+ /**
* A wrapper around GraphicBuffer that contains extra information about how to
* interpret the screenshot GraphicBuffer.
* @hide
@@ -2651,6 +2657,7 @@
* @hide
*/
public Transaction setMetadata(SurfaceControl sc, int key, Parcel data) {
+ sc.checkNotReleased();
nativeSetMetadata(mNativeObject, sc.mNativeObject, key, data);
return this;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c62e69c..7a817b6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1181,7 +1181,8 @@
* a soft input method, so it will be Z-ordered and positioned
* independently of any active input method (typically this means it
* gets Z-ordered on top of the input method, so it can use the full
- * screen for its content and cover the input method if needed.) */
+ * screen for its content and cover the input method if needed. You
+ * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
/** Window flag: this window can never receive touch events. */
@@ -1287,11 +1288,14 @@
* set for you by Window as described in {@link Window#setFlags}.*/
public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
- /** Window flag: When set, input method can't interact with the focusable window
- * and can be placed to use more space and cover the input method.
- * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
- * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
- * flag set.
+ /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with
+ * respect to how this window interacts with the current method. That
+ * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the
+ * window will behave as if it needs to interact with the input method
+ * and thus be placed behind/away from it; if FLAG_NOT_FOCUSABLE is
+ * not set and this flag is set, then the window will behave as if it
+ * doesn't need to interact with the input method and can be placed
+ * to use more space and cover the input method.
*/
public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
@@ -1989,12 +1993,16 @@
*
* @param flags The current window manager flags.
*
- * @return Returns {@code true} if such a window should be behind/interact
- * with an input method, (@code false} if not.
+ * @return Returns true if such a window should be behind/interact
+ * with an input method, false if not.
*/
public static boolean mayUseInputMethod(int flags) {
- return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
- && (flags & FLAG_ALT_FOCUSABLE_IM) != FLAG_ALT_FOCUSABLE_IM;
+ switch (flags&(FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
+ case 0:
+ case FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM:
+ return true;
+ }
+ return false;
}
/**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 54446e1..9c04b39 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -47,6 +47,7 @@
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -61,6 +62,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.widget.EditText;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.MetricsLogger;
@@ -1073,6 +1075,8 @@
} else if (sVerbose) {
Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
}
+ } else if (!virtual && isVisible) {
+ startAutofillIfNeededLocked(view);
}
}
}
@@ -1238,9 +1242,11 @@
return;
}
if (!mEnabled || !isActiveLocked()) {
- if (sVerbose) {
- Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
- + "): ignoring on state " + getStateAsStringLocked());
+ if (!startAutofillIfNeededLocked(view)) {
+ if (sVerbose) {
+ Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+ + "): ignoring on state " + getStateAsStringLocked());
+ }
}
return;
}
@@ -1879,6 +1885,37 @@
}
}
+ @GuardedBy("mLock")
+ private boolean startAutofillIfNeededLocked(View view) {
+ if (mState == STATE_UNKNOWN
+ && mSessionId == NO_SESSION
+ && view instanceof EditText
+ && !TextUtils.isEmpty(((EditText) view).getText())
+ && !view.isFocused()
+ && view.isImportantForAutofill()
+ && view.isLaidOut()
+ && view.isVisibleToUser()) {
+
+ ensureServiceClientAddedIfNeededLocked();
+
+ if (sVerbose) {
+ Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+ }
+ if (mEnabled && !isClientDisablingEnterExitEvent()) {
+ final AutofillId id = view.getAutofillId();
+ final AutofillValue value = view.getAutofillValue();
+ // Starts new session.
+ startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
+ // Updates value.
+ updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
+ /* flags= */ 0);
+ addEnteredIdLocked(id);
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Registers a {@link AutofillCallback} to receive autofill events.
*
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 187ab46..eb0d9bf 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -152,6 +152,9 @@
// handles.
private static final boolean FLAG_USE_MAGNIFIER = true;
+ private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
+ private static final int RECENT_CUT_COPY_DURATION_MS = 15 * 1000; // 15 seconds in millis
+
static final int BLINK = 500;
private static final int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
@@ -326,8 +329,6 @@
// Global listener that detects changes in the global position of the TextView
private PositionListener mPositionListener;
- private float mLastDownPositionX, mLastDownPositionY;
- private float mLastUpPositionX, mLastUpPositionY;
private float mContextMenuAnchorX, mContextMenuAnchorY;
Callback mCustomSelectionActionModeCallback;
Callback mCustomInsertionActionModeCallback;
@@ -336,18 +337,11 @@
@UnsupportedAppUsage
boolean mCreatedWithASelection;
- // Indicates the current tap state (first tap, double tap, or triple click).
- private int mTapState = TAP_STATE_INITIAL;
- private long mLastTouchUpTime = 0;
- private static final int TAP_STATE_INITIAL = 0;
- private static final int TAP_STATE_FIRST_TAP = 1;
- private static final int TAP_STATE_DOUBLE_TAP = 2;
- // Only for mouse input.
- private static final int TAP_STATE_TRIPLE_CLICK = 3;
-
// The button state as of the last time #onTouchEvent is called.
private int mLastButtonState;
+ private final EditorTouchState mTouchState = new EditorTouchState();
+
private Runnable mInsertionActionModeRunnable;
// The span controller helps monitoring the changes to which the Editor needs to react:
@@ -1193,10 +1187,10 @@
logCursor("performLongClick", "handled=%s", handled);
}
// Long press in empty space moves cursor and starts the insertion action mode.
- if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY)
+ if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY())
&& mInsertionControllerEnabled) {
- final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
- mLastDownPositionY);
+ final int offset = mTextView.getOffsetForPosition(mTouchState.getLastDownX(),
+ mTouchState.getLastDownY());
Selection.setSelection((Spannable) mTextView.getText(), offset);
getInsertionController().show();
mIsInsertionActionModeStartPending = true;
@@ -1240,11 +1234,11 @@
}
float getLastUpPositionX() {
- return mLastUpPositionX;
+ return mTouchState.getLastUpX();
}
float getLastUpPositionY() {
- return mLastUpPositionY;
+ return mTouchState.getLastUpY();
}
private long getLastTouchOffsets() {
@@ -1279,6 +1273,9 @@
// Has to be done before onTakeFocus, which can be overloaded.
final int lastTapPosition = getLastTapPosition();
if (lastTapPosition >= 0) {
+ if (TextView.DEBUG_CURSOR) {
+ logCursor("onFocusChanged", "setting cursor position: %d", lastTapPosition);
+ }
Selection.setSelection((Spannable) mTextView.getText(), lastTapPosition);
}
@@ -1443,39 +1440,6 @@
}
}
- private void updateTapState(MotionEvent event) {
- final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
- // Detect double tap and triple click.
- if (((mTapState == TAP_STATE_FIRST_TAP)
- || ((mTapState == TAP_STATE_DOUBLE_TAP) && isMouse))
- && (SystemClock.uptimeMillis() - mLastTouchUpTime)
- <= ViewConfiguration.getDoubleTapTimeout()) {
- if (mTapState == TAP_STATE_FIRST_TAP) {
- mTapState = TAP_STATE_DOUBLE_TAP;
- } else {
- mTapState = TAP_STATE_TRIPLE_CLICK;
- }
- if (TextView.DEBUG_CURSOR) {
- logCursor("updateTapState", "ACTION_DOWN: %s tap detected",
- (mTapState == TAP_STATE_DOUBLE_TAP ? "double" : "triple"));
- }
- } else {
- mTapState = TAP_STATE_FIRST_TAP;
- if (TextView.DEBUG_CURSOR) {
- logCursor("updateTapState", "ACTION_DOWN: first tap detected");
- }
- }
- }
- if (action == MotionEvent.ACTION_UP) {
- mLastTouchUpTime = SystemClock.uptimeMillis();
- if (TextView.DEBUG_CURSOR) {
- logCursor("updateTapState", "ACTION_UP");
- }
- }
- }
-
private boolean shouldFilterOutTouchEvent(MotionEvent event) {
if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
return false;
@@ -1503,7 +1467,8 @@
}
return;
}
- updateTapState(event);
+ ViewConfiguration viewConfiguration = ViewConfiguration.get(mTextView.getContext());
+ mTouchState.update(event, viewConfiguration);
updateFloatingToolbarVisibility(event);
if (hasSelectionController()) {
@@ -1515,15 +1480,7 @@
mShowSuggestionRunnable = null;
}
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mLastUpPositionX = event.getX();
- mLastUpPositionY = event.getY();
- }
-
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mLastDownPositionX = event.getX();
- mLastDownPositionY = event.getY();
-
// Reset this state; it will be re-set if super.onTouchEvent
// causes focus to move to the view.
mTouchFocusSelected = false;
@@ -5067,7 +5024,10 @@
public boolean onTouchEvent(MotionEvent ev) {
if (TextView.DEBUG_CURSOR) {
logCursor(this.getClass().getSimpleName() + ": HandleView: onTouchEvent",
- MotionEvent.actionToString(ev.getActionMasked()));
+ "%d: %s (%f,%f)",
+ ev.getSequenceNumber(),
+ MotionEvent.actionToString(ev.getActionMasked()),
+ ev.getX(), ev.getY());
}
updateFloatingToolbarVisibility(ev);
@@ -5145,56 +5105,14 @@
}
private class InsertionHandleView extends HandleView {
- private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
- private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
-
// Used to detect taps on the insertion handle, which will affect the insertion action mode
- private float mDownPositionX, mDownPositionY;
+ private float mLastDownRawX, mLastDownRawY;
private Runnable mHider;
public InsertionHandleView(Drawable drawable) {
super(drawable, drawable, com.android.internal.R.id.insertion_handle);
}
- @Override
- public void show() {
- super.show();
-
- final long durationSinceCutOrCopy =
- SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
-
- // Cancel the single tap delayed runnable.
- if (mInsertionActionModeRunnable != null
- && ((mTapState == TAP_STATE_DOUBLE_TAP)
- || (mTapState == TAP_STATE_TRIPLE_CLICK)
- || isCursorInsideEasyCorrectionSpan())) {
- mTextView.removeCallbacks(mInsertionActionModeRunnable);
- }
-
- // Prepare and schedule the single tap runnable to run exactly after the double tap
- // timeout has passed.
- if ((mTapState != TAP_STATE_DOUBLE_TAP) && (mTapState != TAP_STATE_TRIPLE_CLICK)
- && !isCursorInsideEasyCorrectionSpan()
- && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
- if (mTextActionMode == null) {
- if (mInsertionActionModeRunnable == null) {
- mInsertionActionModeRunnable = new Runnable() {
- @Override
- public void run() {
- startInsertionActionMode();
- }
- };
- }
- mTextView.postDelayed(
- mInsertionActionModeRunnable,
- ViewConfiguration.getDoubleTapTimeout() + 1);
- }
-
- }
-
- hideAfterDelay();
- }
-
private void hideAfterDelay() {
if (mHider == null) {
mHider = new Runnable() {
@@ -5250,8 +5168,8 @@
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mDownPositionX = ev.getRawX();
- mDownPositionY = ev.getRawY();
+ mLastDownRawX = ev.getRawX();
+ mLastDownRawY = ev.getRawY();
updateMagnifier(ev);
break;
@@ -5261,8 +5179,8 @@
case MotionEvent.ACTION_UP:
if (!offsetHasBeenChanged()) {
- final float deltaX = mDownPositionX - ev.getRawX();
- final float deltaY = mDownPositionY - ev.getRawY();
+ final float deltaX = mLastDownRawX - ev.getRawX();
+ final float deltaY = mLastDownRawY - ev.getRawY();
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
final ViewConfiguration viewConfiguration = ViewConfiguration.get(
@@ -5804,6 +5722,37 @@
public void show() {
getHandle().show();
+ final long durationSinceCutOrCopy =
+ SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
+
+ // Cancel the single tap delayed runnable.
+ if (mInsertionActionModeRunnable != null
+ && (mTouchState.isMultiTap() || isCursorInsideEasyCorrectionSpan())) {
+ mTextView.removeCallbacks(mInsertionActionModeRunnable);
+ }
+
+ // Prepare and schedule the single tap runnable to run exactly after the double tap
+ // timeout has passed.
+ if (!mTouchState.isMultiTap()
+ && !isCursorInsideEasyCorrectionSpan()
+ && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION_MS)) {
+ if (mTextActionMode == null) {
+ if (mInsertionActionModeRunnable == null) {
+ mInsertionActionModeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ startInsertionActionMode();
+ }
+ };
+ }
+ mTextView.postDelayed(
+ mInsertionActionModeRunnable,
+ ViewConfiguration.getDoubleTapTimeout() + 1);
+ }
+ }
+
+ getHandle().hideAfterDelay();
+
if (mSelectionModifierCursorController != null) {
mSelectionModifierCursorController.hide();
}
@@ -5870,7 +5819,6 @@
// The offsets of that last touch down event. Remembered to start selection there.
private int mMinTouchOffset, mMaxTouchOffset;
- private float mDownPositionX, mDownPositionY;
private boolean mGestureStayedInTapRegion;
// Where the user first starts the drag motion.
@@ -5940,13 +5888,18 @@
}
public void enterDrag(int dragAcceleratorMode) {
+ if (TextView.DEBUG_CURSOR) {
+ logCursor("SelectionModifierCursorController: enterDrag",
+ "starting selection drag: mode=%s", dragAcceleratorMode);
+ }
+
// Just need to init the handles / hide insertion cursor.
show();
mDragAcceleratorMode = dragAcceleratorMode;
// Start location of selection.
- mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX,
- mLastDownPositionY);
- mLineSelectionIsOn = mTextView.getLineAtCoordinate(mLastDownPositionY);
+ mStartOffset = mTextView.getOffsetForPosition(mTouchState.getLastDownX(),
+ mTouchState.getLastDownY());
+ mLineSelectionIsOn = mTextView.getLineAtCoordinate(mTouchState.getLastDownY());
// Don't show the handles until user has lifted finger.
hide();
@@ -5974,36 +5927,20 @@
eventX, eventY);
// Double tap detection
- if (mGestureStayedInTapRegion) {
- if (mTapState == TAP_STATE_DOUBLE_TAP
- || mTapState == TAP_STATE_TRIPLE_CLICK) {
- final float deltaX = eventX - mDownPositionX;
- final float deltaY = eventY - mDownPositionY;
- final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
-
- ViewConfiguration viewConfiguration = ViewConfiguration.get(
- mTextView.getContext());
- int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
- boolean stayedInArea =
- distanceSquared < doubleTapSlop * doubleTapSlop;
-
- if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) {
- if (TextView.DEBUG_CURSOR) {
- logCursor("SelectionModifierCursorController: onTouchEvent",
- "ACTION_DOWN: select and start drag");
- }
- if (mTapState == TAP_STATE_DOUBLE_TAP) {
- selectCurrentWordAndStartDrag();
- } else if (mTapState == TAP_STATE_TRIPLE_CLICK) {
- selectCurrentParagraphAndStartDrag();
- }
- mDiscardNextActionUp = true;
- }
+ if (mGestureStayedInTapRegion
+ && mTouchState.isMultiTapInSameArea()
+ && (isMouse || isPositionOnText(eventX, eventY))) {
+ if (TextView.DEBUG_CURSOR) {
+ logCursor("SelectionModifierCursorController: onTouchEvent",
+ "ACTION_DOWN: select and start drag");
}
+ if (mTouchState.isDoubleTap()) {
+ selectCurrentWordAndStartDrag();
+ } else if (mTouchState.isTripleClick()) {
+ selectCurrentParagraphAndStartDrag();
+ }
+ mDiscardNextActionUp = true;
}
-
- mDownPositionX = eventX;
- mDownPositionY = eventY;
mGestureStayedInTapRegion = true;
mHaventMovedEnoughToStartDrag = true;
}
@@ -6025,8 +5962,8 @@
final int touchSlop = viewConfig.getScaledTouchSlop();
if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
- final float deltaX = eventX - mDownPositionX;
- final float deltaY = eventY - mDownPositionY;
+ final float deltaX = eventX - mTouchState.getLastDownX();
+ final float deltaY = eventY - mTouchState.getLastDownY();
final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (mGestureStayedInTapRegion) {
@@ -7164,7 +7101,7 @@
}
}
- private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
+ static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
if (msgFormat == null) {
Log.d(TAG, location);
} else {
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
new file mode 100644
index 0000000..f880939
--- /dev/null
+++ b/core/java/android/widget/EditorTouchState.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 android.widget;
+
+import static android.widget.Editor.logCursor;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.IntDef;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Helper class used by {@link Editor} to track state for touch events.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public class EditorTouchState {
+ private float mLastDownX, mLastDownY;
+ private float mLastUpX, mLastUpY;
+ private long mLastUpMillis;
+
+ @IntDef({MultiTapStatus.NONE, MultiTapStatus.FIRST_TAP, MultiTapStatus.DOUBLE_TAP,
+ MultiTapStatus.TRIPLE_CLICK})
+ @Retention(RetentionPolicy.SOURCE)
+ @VisibleForTesting
+ public @interface MultiTapStatus {
+ int NONE = 0;
+ int FIRST_TAP = 1;
+ int DOUBLE_TAP = 2;
+ int TRIPLE_CLICK = 3; // Only for mouse input.
+ }
+ @MultiTapStatus
+ private int mMultiTapStatus = MultiTapStatus.NONE;
+ private boolean mMultiTapInSameArea;
+
+ public float getLastDownX() {
+ return mLastDownX;
+ }
+
+ public float getLastDownY() {
+ return mLastDownY;
+ }
+
+ public float getLastUpX() {
+ return mLastUpX;
+ }
+
+ public float getLastUpY() {
+ return mLastUpY;
+ }
+
+ public boolean isDoubleTap() {
+ return mMultiTapStatus == MultiTapStatus.DOUBLE_TAP;
+ }
+
+ public boolean isTripleClick() {
+ return mMultiTapStatus == MultiTapStatus.TRIPLE_CLICK;
+ }
+
+ public boolean isMultiTap() {
+ return mMultiTapStatus == MultiTapStatus.DOUBLE_TAP
+ || mMultiTapStatus == MultiTapStatus.TRIPLE_CLICK;
+ }
+
+ public boolean isMultiTapInSameArea() {
+ return isMultiTap() && mMultiTapInSameArea;
+ }
+
+ /**
+ * Updates the state based on the new event.
+ */
+ public void update(MotionEvent event, ViewConfiguration viewConfiguration) {
+ final int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+ final long millisSinceLastUp = event.getEventTime() - mLastUpMillis;
+ // Detect double tap and triple click.
+ if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout()
+ && (mMultiTapStatus == MultiTapStatus.FIRST_TAP
+ || (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) {
+ if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) {
+ mMultiTapStatus = MultiTapStatus.DOUBLE_TAP;
+ } else {
+ mMultiTapStatus = MultiTapStatus.TRIPLE_CLICK;
+ }
+ final float deltaX = event.getX() - mLastDownX;
+ final float deltaY = event.getY() - mLastDownY;
+ final int distanceSquared = (int) ((deltaX * deltaX) + (deltaY * deltaY));
+ int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
+ mMultiTapInSameArea = distanceSquared < doubleTapSlop * doubleTapSlop;
+ if (TextView.DEBUG_CURSOR) {
+ String status = isDoubleTap() ? "double" : "triple";
+ String inSameArea = mMultiTapInSameArea ? "in same area" : "not in same area";
+ logCursor("EditorTouchState", "ACTION_DOWN: %s tap detected, %s",
+ status, inSameArea);
+ }
+ } else {
+ mMultiTapStatus = MultiTapStatus.FIRST_TAP;
+ mMultiTapInSameArea = false;
+ if (TextView.DEBUG_CURSOR) {
+ logCursor("EditorTouchState", "ACTION_DOWN: first tap detected");
+ }
+ }
+ mLastDownX = event.getX();
+ mLastDownY = event.getY();
+ } else if (action == MotionEvent.ACTION_UP) {
+ if (TextView.DEBUG_CURSOR) {
+ logCursor("EditorTouchState", "ACTION_UP");
+ }
+ mLastUpX = event.getX();
+ mLastUpY = event.getY();
+ mLastUpMillis = event.getEventTime();
+ }
+ }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 90e8ef2..ee169f2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10860,7 +10860,10 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
if (DEBUG_CURSOR) {
- logCursor("onTouchEvent", MotionEvent.actionToString(event.getActionMasked()));
+ logCursor("onTouchEvent", "%d: %s (%f,%f)",
+ event.getSequenceNumber(),
+ MotionEvent.actionToString(event.getActionMasked()),
+ event.getX(), event.getY());
}
final int action = event.getActionMasked();
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 15b1d75..2f1a15f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,10 +19,10 @@
import com.android.internal.os.BatteryStatsImpl;
import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.net.wifi.WifiActivityEnergyInfo;
import android.os.ParcelFileDescriptor;
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.os.connectivity.GpsBatteryStats;
import android.os.health.HealthStatsParceler;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index e48e2df..16628d7 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -30,6 +30,7 @@
private final @Nullable String mName;
private final int mEnableAfterTargetSdk;
private final boolean mDisabled;
+ private final @Nullable String mDescription;
public long getId() {
return mChangeId;
@@ -48,12 +49,18 @@
return mDisabled;
}
+ public String getDescription() {
+ return mDescription;
+ }
+
public CompatibilityChangeInfo(
- Long changeId, String name, int enableAfterTargetSdk, boolean disabled) {
+ Long changeId, String name, int enableAfterTargetSdk, boolean disabled,
+ String description) {
this.mChangeId = changeId;
this.mName = name;
this.mEnableAfterTargetSdk = enableAfterTargetSdk;
this.mDisabled = disabled;
+ this.mDescription = description;
}
private CompatibilityChangeInfo(Parcel in) {
@@ -61,6 +68,7 @@
mName = in.readString();
mEnableAfterTargetSdk = in.readInt();
mDisabled = in.readBoolean();
+ mDescription = in.readString();
}
@Override
@@ -74,6 +82,7 @@
dest.writeString(mName);
dest.writeInt(mEnableAfterTargetSdk);
dest.writeBoolean(mDisabled);
+ dest.writeString(mDescription);
}
public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 25fa65e..bc44fcf 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -33,7 +33,6 @@
import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
-import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -56,6 +55,7 @@
import android.os.WorkSource.WorkChain;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
import android.telephony.CellSignalStrength;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 084a3cc..01f5743 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,7 +21,6 @@
import android.telephony.CellInfo;
import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.ServiceState;
@@ -44,7 +43,6 @@
void onDataConnectionStateChanged(int state, int networkType);
void onDataActivity(int direction);
void onSignalStrengthsChanged(in SignalStrength signalStrength);
- void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
void onOtaspChanged(in int otaspMode);
void onCellInfoChanged(in List<CellInfo> cellInfo);
void onPreciseCallStateChanged(in PreciseCallState callState);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 9ae0ba5..2f34aa0 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -24,7 +24,6 @@
import android.telephony.CellInfo;
import android.telephony.ims.ImsReasonInfo;
import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.emergency.EmergencyNumber;
@@ -78,8 +77,6 @@
void notifyOtaspChanged(in int subId, in int otaspMode);
@UnsupportedAppUsage
void notifyCellInfo(in List<CellInfo> cellInfo);
- void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
- in List<PhysicalChannelConfig> configs);
void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
int foregroundCallState, int backgroundCallState);
void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index b0888f2..121ae1a 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -29,7 +29,7 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.util.Slog;
+import android.util.Log;
import java.util.Stack;
@@ -841,7 +841,7 @@
msg.replyTo = sm.mMessenger;
synchronized (sm.mHandler.mLockObject) {
if (sm.mHandler.mResultMsg != null) {
- Slog.wtf(TAG, "mResultMsg should be null here");
+ Log.wtf(TAG, "mResultMsg should be null here");
sm.mHandler.mResultMsg = null;
}
dstMessenger.send(msg);
@@ -851,9 +851,9 @@
}
}
} catch (InterruptedException e) {
- Slog.e(TAG, "error in sendMessageSynchronously", e);
+ Log.e(TAG, "error in sendMessageSynchronously", e);
} catch (RemoteException e) {
- Slog.e(TAG, "error in sendMessageSynchronously", e);
+ Log.e(TAG, "error in sendMessageSynchronously", e);
}
sm.recycle();
return resultMsg;
@@ -939,7 +939,7 @@
* @param s
*/
private static void log(String s) {
- Slog.d(TAG, s);
+ Log.d(TAG, s);
}
private final class DeathMonitor implements IBinder.DeathRecipient {
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
index e39a2bf..58e4a60 100644
--- a/core/java/com/android/internal/util/AsyncService.java
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -22,7 +22,7 @@
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
-import android.util.Slog;
+import android.util.Log;
/**
* A service that receives Intents and IBinder transactions
@@ -92,7 +92,7 @@
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (DBG) Slog.d(TAG, "onStartCommand");
+ if (DBG) Log.d(TAG, "onStartCommand");
Message msg = mHandler.obtainMessage();
msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT;
@@ -111,7 +111,7 @@
*/
@Override
public void onDestroy() {
- if (DBG) Slog.d(TAG, "onDestroy");
+ if (DBG) Log.d(TAG, "onDestroy");
Message msg = mHandler.obtainMessage();
msg.what = CMD_ASYNC_SERVICE_DESTROY;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 57bfcac..2ac2975 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -36,6 +36,8 @@
import dalvik.annotation.compat.UnsupportedAppUsage;
+import java.io.IOException;
+
public class BaseIWindow extends IWindow.Stub {
@UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
@@ -101,6 +103,13 @@
@Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+ if (out != null) {
+ try {
+ out.closeWithError("Unsupported command " + command);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
}
@Override
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ce2717b..9f31b59 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -77,12 +77,14 @@
message ActivityDisplayProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
- optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
+ // To be removed soon.
+ optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
optional int32 id = 2;
repeated ActivityStackProto stacks = 3;
optional int32 focused_stack_id = 4;
optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
optional bool single_task_instance = 6;
+ optional .com.android.server.wm.DisplayContentProto display = 7;
}
message ActivityStackProto {
diff --git a/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto
new file mode 100644
index 0000000..138782b
--- /dev/null
+++ b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto
@@ -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.
+ */
+
+syntax = "proto2";
+package android.stats.mediaprovider;
+option java_multiple_files = true;
+
+enum VolumeType {
+ // Volume is unknown
+ UNKNOWN = 0;
+ // Volume is MediaStore.VOLUME_INTERNAL
+ INTERNAL = 1;
+ // Volume is MediaStore.VOLUME_EXTERNAL_PRIMARY
+ EXTERNAL_PRIMARY = 2;
+ // Volume is non-primary external storage
+ EXTERNAL_OTHER = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9dc2319..f6e91ef 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5070,6 +5070,10 @@
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+ <service android:name="com.android.server.usage.UsageStatsIdleService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
<service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 234ffee..38d3e9c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3750,6 +3750,14 @@
</p>
-->
<attr name="canRequestFingerprintGestures" format="boolean" />
+
+ <!-- Animated image of the accessibility service purpose or behavior, to help users
+ understand how the service can help them.-->
+ <attr name="animatedImageDrawable" format="reference"/>
+ <!-- Html description of the accessibility service, to help users understand
+ how the service can help them.-->
+ <attr name="htmlDescription" format="string"/>
+
<!-- Short description of the accessibility service purpose or behavior.-->
<attr name="description" />
<!-- Brief summary of the accessibility service purpose or behavior. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc9fa85..10a7584 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3695,6 +3695,8 @@
instead of 'Emergency calls only' when SIM is unready. -->
<string-array translatable="false" name="config_display_no_service_when_sim_unready">
<item>"DE"</item>
+ <item>"GB"</item>
+ <item>"JP"</item>
</string-array>
<!-- Class names of device specific services inheriting com.android.server.SystemService. The
@@ -4179,4 +4181,10 @@
<string-array name="config_integrityRuleProviderPackages" translatable="false">
<!-- Add packages here -->
</string-array>
+
+ <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. -->
+ <bool name="config_bugReportHandlerEnabled">false</bool>
+
+ <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. -->
+ <string name="config_defaultBugReportHandlerApp" translatable="false"></string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 57e0e1b..78c4efe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3002,6 +3002,8 @@
<public name="forceQueryable" />
<!-- @hide @SystemApi -->
<public name="resourcesMap" />
+ <public name="animatedImageDrawable"/>
+ <public name="htmlDescription"/>
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dcf7379..0f5da39 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1014,23 +1014,29 @@
<string name="permdesc_readContacts" product="tablet">Allows the app to read
data about your contacts stored on your tablet, including the frequency
with which you\'ve called, emailed, or communicated in other ways with
- specific individuals. This permission allows apps to save your contact
+ specific individuals. Apps will also have access to the accounts on your
+ tablet that have created contacts. This may include accounts created by
+ apps you have installed. This permission allows apps to save your contact
data, and malicious apps may share contact data without your
knowledge.</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readContacts" product="tv">Allows the app to read
data about your contacts stored on your Android TV device, including the frequency
with which you\'ve called, emailed, or communicated in other ways with
- specific individuals. This permission allows apps to save your contact
- data, and malicious apps may share contact data without your
+ specific individuals. Apps will also have access to the accounts on your
+ Android TV device that have created contacts. This may include accounts
+ created by apps you have installed. This permission allows apps to save
+ your contact data, and malicious apps may share contact data without your
knowledge.</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readContacts" product="default">Allows the app to
read data about your contacts stored on your phone, including the
frequency with which you\'ve called, emailed, or communicated in other ways
- with specific individuals. This permission allows apps to save your
- contact data, and malicious apps may share contact data without your
- knowledge.</string>
+ with specific individuals. Apps will also have access to the accounts
+ on your phone that have created contacts. This may include accounts
+ created by apps you have installed. This permission allows apps to
+ save your contact data, and malicious apps may share contact data
+ without your knowledge.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_writeContacts">modify your contacts</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c6c9094..083f33c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3751,6 +3751,10 @@
<java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" />
+ <!-- For bug report handler -->
+ <java-symbol type="bool" name="config_bugReportHandlerEnabled" />
+ <java-symbol type="string" name="config_defaultBugReportHandlerApp" />
+
<java-symbol type="string" name="usb_device_resolve_prompt_warn" />
<!-- For Accessibility system actions -->
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
index e01e254..0c3d34e 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
@@ -31,7 +31,6 @@
import androidx.test.runner.lifecycle.Stage
import com.google.common.truth.Truth.assertThat
import org.junit.After
-import org.junit.Assume
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -43,7 +42,6 @@
import java.util.concurrent.FutureTask
import java.util.concurrent.TimeUnit
-// @Ignore("UiAutomation is crashing with not connected, not sure why")
@RunWith(Parameterized::class)
class ResourceLoaderChangesTest : ResourceLoaderTestBase() {
@@ -75,7 +73,7 @@
@Before
@After
fun disableOverlay() {
-// enableOverlay(OVERLAY_PACKAGE, false)
+ enableOverlay(OVERLAY_PACKAGE, false)
}
@Test
@@ -156,25 +154,32 @@
// All these tests assert for the exact same loaders/values, so extract that logic out
private fun verifySameBeforeAndAfter(block: () -> Resources) {
- // TODO(chiuwinson): atest doesn't work with @Ignore, UiAutomation not connected error
- Assume.assumeFalse(true)
+ fun Resources.resource() = this.getString(android.R.string.cancel)
+ fun Resources.asset() = this.assets.open("Asset.txt").reader().readText()
- val originalValue = resources.getString(android.R.string.cancel)
+ val originalResource = resources.resource()
+ val originalAsset = resources.asset()
- val loader = "stringOne".openLoader()
- addLoader(loader)
+ val loaderResource = "stringOne".openLoader()
+ val loaderAsset = "assetOne".openLoader(dataType = DataType.ASSET)
+ addLoader(loaderResource)
+ addLoader(loaderAsset)
val oldLoaders = resources.loaders
- val oldValue = resources.getString(android.R.string.cancel)
+ val oldResource = resources.resource()
+ val oldAsset = resources.asset()
- assertThat(oldValue).isNotEqualTo(originalValue)
+ assertThat(oldResource).isNotEqualTo(originalResource)
+ assertThat(oldAsset).isNotEqualTo(originalAsset)
val newResources = block()
val newLoaders = newResources.loaders
- val newValue = newResources.getString(android.R.string.cancel)
+ val newResource = newResources.resource()
+ val newAsset = newResources.asset()
- assertThat(newValue).isEqualTo(oldValue)
+ assertThat(newResource).isEqualTo(oldResource)
+ assertThat(newAsset).isEqualTo(oldAsset)
assertThat(newLoaders).isEqualTo(oldLoaders)
}
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
new file mode 100644
index 0000000..de6f8f7
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+
+public class ManualTimeSuggestionTest {
+
+ private static final TimestampedValue<Long> ARBITRARY_TIME =
+ new TimestampedValue<>(1111L, 2222L);
+
+ @Test
+ public void testEquals() {
+ ManualTimeSuggestion one = new ManualTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, one);
+
+ ManualTimeSuggestion two = new ManualTimeSuggestion(ARBITRARY_TIME);
+ assertEquals(one, two);
+ assertEquals(two, one);
+
+ TimestampedValue<Long> differentTime = new TimestampedValue<>(
+ ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+ ARBITRARY_TIME.getValue());
+ ManualTimeSuggestion three = new ManualTimeSuggestion(differentTime);
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+
+ // DebugInfo must not be considered in equals().
+ one.addDebugInfo("Debug info 1");
+ two.addDebugInfo("Debug info 2");
+ assertEquals(one, two);
+ }
+
+ @Test
+ public void testParcelable() {
+ ManualTimeSuggestion suggestion = new ManualTimeSuggestion(ARBITRARY_TIME);
+ assertRoundTripParcelable(suggestion);
+
+ // DebugInfo should also be stored (but is not checked by equals()
+ suggestion.addDebugInfo("This is debug info");
+ ManualTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+ assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index c9a86dc..bee270e 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -16,11 +16,12 @@
package android.app.timedetector;
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.TimestampedValue;
import org.junit.Test;
@@ -30,53 +31,67 @@
@Test
public void testEquals() {
- PhoneTimeSuggestion one = new PhoneTimeSuggestion(PHONE_ID);
- assertEquals(one, one);
+ PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ assertEquals(one, one);
+ }
- PhoneTimeSuggestion two = new PhoneTimeSuggestion(PHONE_ID);
- assertEquals(one, two);
- assertEquals(two, one);
+ PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ PhoneTimeSuggestion two = builder2.build();
+ assertEquals(one, two);
+ assertEquals(two, one);
+ }
- one.setUtcTime(new TimestampedValue<>(1111L, 2222L));
- assertEquals(one, one);
+ builder1.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ assertEquals(one, one);
+ }
- two.setUtcTime(new TimestampedValue<>(1111L, 2222L));
- assertEquals(one, two);
- assertEquals(two, one);
+ builder2.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ PhoneTimeSuggestion two = builder2.build();
+ assertEquals(one, two);
+ assertEquals(two, one);
+ }
- PhoneTimeSuggestion three = new PhoneTimeSuggestion(PHONE_ID + 1);
- three.setUtcTime(new TimestampedValue<>(1111L, 2222L));
- assertNotEquals(one, three);
- assertNotEquals(three, one);
+ PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(PHONE_ID + 1);
+ builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ PhoneTimeSuggestion three = builder3.build();
+ assertNotEquals(one, three);
+ assertNotEquals(three, one);
+ }
// DebugInfo must not be considered in equals().
- one.addDebugInfo("Debug info 1");
- two.addDebugInfo("Debug info 2");
- assertEquals(one, two);
+ builder1.addDebugInfo("Debug info 1");
+ builder2.addDebugInfo("Debug info 2");
+ {
+ PhoneTimeSuggestion one = builder1.build();
+ PhoneTimeSuggestion two = builder2.build();
+ assertEquals(one, two);
+ }
}
@Test
public void testParcelable() {
- PhoneTimeSuggestion one = new PhoneTimeSuggestion(PHONE_ID);
- assertEquals(one, roundTripParcelable(one));
+ PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(PHONE_ID);
+ assertRoundTripParcelable(builder.build());
- one.setUtcTime(new TimestampedValue<>(1111L, 2222L));
- assertEquals(one, roundTripParcelable(one));
+ builder.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+ assertRoundTripParcelable(builder.build());
// DebugInfo should also be stored (but is not checked by equals()
- one.addDebugInfo("This is debug info");
- PhoneTimeSuggestion two = roundTripParcelable(one);
- assertEquals(one.getDebugInfo(), two.getDebugInfo());
- }
-
- @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(PhoneTimeSuggestion.CREATOR);
- parcel.recycle();
- return toReturn;
+ {
+ PhoneTimeSuggestion suggestion1 = builder.build();
+ builder.addDebugInfo("This is debug info");
+ PhoneTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1);
+ assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo());
+ }
}
}
diff --git a/core/tests/coretests/src/android/service/controls/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
new file mode 100644
index 0000000..ef4912f
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.service.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlActionTest {
+
+ private static final String TEST_ID = "TEST_ID";
+
+ @Test
+ public void testUnparcelingCorrectClass_boolean() {
+ ControlAction toParcel = new BooleanAction(TEST_ID, true);
+
+ ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlAction.TYPE_BOOLEAN, fromParcel.getActionType());
+ assertTrue(fromParcel instanceof BooleanAction);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_float() {
+ ControlAction toParcel = new FloatAction(TEST_ID, 1);
+
+ ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlAction.TYPE_FLOAT, fromParcel.getActionType());
+ assertTrue(fromParcel instanceof FloatAction);
+ }
+
+ private ControlAction parcelAndUnparcel(ControlAction toParcel) {
+ Parcel parcel = Parcel.obtain();
+
+ assertNotNull(parcel);
+
+ parcel.setDataPosition(0);
+ toParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ return ControlAction.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
new file mode 100644
index 0000000..4fa4e1d
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.service.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.DrawableRes;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlTemplateTest {
+
+ private static final String PACKAGE_NAME = "com.android.frameworks.coretests";
+ private static final @DrawableRes int TEST_ICON_ID = R.drawable.box;
+ private static final String TEST_ID = "TEST_ID";
+ private static final CharSequence TEST_CONTENT_DESCRIPTION = "TEST_CONTENT_DESCRIPTION";
+ private Icon mIcon;
+ private ControlButton mControlButton;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mIcon = Icon.createWithResource(PACKAGE_NAME, TEST_ICON_ID);
+ mControlButton = new ControlButton(true, mIcon, TEST_CONTENT_DESCRIPTION);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_none() {
+ ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE;
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.NO_TEMPLATE, fromParcel);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_toggle() {
+ ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_TOGGLE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof ToggleTemplate);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_range() {
+ ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_RANGE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof RangeTemplate);
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_minMax() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 2, 0, 1, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_minCurrent() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, -1, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_maxCurrent() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 3, 1, "%f");
+ }
+
+ @Test(expected = InvalidParameterException.class)
+ public void testRangeParameters_negativeStep() {
+ RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 1, -1, "%f");
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_thumbnail() {
+ ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_CONTENT_DESCRIPTION);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof ThumbnailTemplate);
+ }
+
+ @Test
+ public void testUnparcelingCorrectClass_discreteToggle() {
+ ControlTemplate toParcel =
+ new DiscreteToggleTemplate(TEST_ID, mControlButton, mControlButton);
+
+ ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+ assertEquals(ControlTemplate.TYPE_DISCRETE_TOGGLE, fromParcel.getTemplateType());
+ assertTrue(fromParcel instanceof DiscreteToggleTemplate);
+ }
+
+ private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) {
+ Parcel parcel = Parcel.obtain();
+
+ assertNotNull(parcel);
+
+ parcel.setDataPosition(0);
+ toParcel.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ return ControlTemplate.CREATOR.createFromParcel(parcel);
+ }
+}
diff --git a/core/tests/coretests/src/android/util/StatsEventTest.java b/core/tests/coretests/src/android/util/StatsEventTest.java
index 93f11db..097bada 100644
--- a/core/tests/coretests/src/android/util/StatsEventTest.java
+++ b/core/tests/coretests/src/android/util/StatsEventTest.java
@@ -53,8 +53,8 @@
final ByteBuffer buffer =
ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
- assertWithMessage("Root element in buffer is not TYPE_ERRORS")
- .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
assertWithMessage("Incorrect number of elements in root object")
.that(buffer.get()).isEqualTo(3);
@@ -71,6 +71,9 @@
assertWithMessage("Incorrect atom id")
.that(buffer.getInt()).isEqualTo(expectedAtomId);
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+
final int errorMask = buffer.getInt();
assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask")
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
new file mode 100644
index 0000000..6d50e3a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.widget;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.EditorTouchState.MultiTapStatus;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class EditorTouchStateTest {
+
+ private EditorTouchState mTouchState;
+ private ViewConfiguration mConfig;
+
+ @Before
+ public void before() throws Exception {
+ mTouchState = new EditorTouchState();
+ mConfig = new ViewConfiguration();
+ }
+
+ @Test
+ public void testUpdate_singleTap() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+ // Simulate an ACTION_UP event.
+ long event2Time = 1001;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+ // Generate an ACTION_DOWN event whose time is after the double-tap timeout.
+ long event3Time = event2Time + ViewConfiguration.getDoubleTapTimeout() + 1;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+ mTouchState.update(event3, mConfig);
+ assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
+ }
+
+ @Test
+ public void testUpdate_doubleTap_sameArea() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+ // Simulate an ACTION_UP event.
+ long event2Time = 1001;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
+ long event3Time = 1002;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+ mTouchState.update(event3, mConfig);
+ assertTap(mTouchState, 22f, 33f, 20f, 30f,
+ MultiTapStatus.DOUBLE_TAP, true);
+ }
+
+ @Test
+ public void testUpdate_doubleTap_notSameArea() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+ // Simulate an ACTION_UP event.
+ long event2Time = 1001;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+ // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
+ long event3Time = 1002;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 200f, 300f);
+ mTouchState.update(event3, mConfig);
+ assertTap(mTouchState, 200f, 300f, 20f, 30f,
+ MultiTapStatus.DOUBLE_TAP, false);
+
+ // Simulate an ACTION_UP event.
+ long event4Time = 1003;
+ MotionEvent event4 = upEvent(event3Time, event4Time, 200f, 300f);
+ mTouchState.update(event4, mConfig);
+ assertTap(mTouchState, 200f, 300f, 200f, 300f,
+ MultiTapStatus.DOUBLE_TAP, false);
+ }
+
+ @Test
+ public void testUpdate_tripleClick_mouse() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ event1.setSource(InputDevice.SOURCE_MOUSE);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+ // Simulate an ACTION_UP event.
+ long event2Time = 1001;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ event2.setSource(InputDevice.SOURCE_MOUSE);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+ // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
+ long event3Time = 1002;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 21f, 31f);
+ event3.setSource(InputDevice.SOURCE_MOUSE);
+ mTouchState.update(event3, mConfig);
+ assertTap(mTouchState, 21f, 31f, 20f, 30f,
+ MultiTapStatus.DOUBLE_TAP, true);
+
+ // Simulate an ACTION_UP event.
+ long event4Time = 1003;
+ MotionEvent event4 = upEvent(event3Time, event4Time, 21f, 31f);
+ event4.setSource(InputDevice.SOURCE_MOUSE);
+ mTouchState.update(event4, mConfig);
+ assertTap(mTouchState, 21f, 31f, 21f, 31f,
+ MultiTapStatus.DOUBLE_TAP, true);
+
+ // Generate a third ACTION_DOWN event whose time is within the double-tap timeout.
+ long event5Time = 1004;
+ MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f);
+ event5.setSource(InputDevice.SOURCE_MOUSE);
+ mTouchState.update(event5, mConfig);
+ assertTap(mTouchState, 22f, 32f, 21f, 31f,
+ MultiTapStatus.TRIPLE_CLICK, true);
+ }
+
+ @Test
+ public void testUpdate_tripleClick_touch() throws Exception {
+ // Simulate an ACTION_DOWN event.
+ long event1Time = 1000;
+ MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+ mTouchState.update(event1, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+ // Simulate an ACTION_UP event.
+ long event2Time = 1001;
+ MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+ mTouchState.update(event2, mConfig);
+ assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+ // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
+ long event3Time = 1002;
+ MotionEvent event3 = downEvent(event3Time, event3Time, 21f, 31f);
+ mTouchState.update(event3, mConfig);
+ assertTap(mTouchState, 21f, 31f, 20f, 30f,
+ MultiTapStatus.DOUBLE_TAP, true);
+
+ // Simulate an ACTION_UP event.
+ long event4Time = 1003;
+ MotionEvent event4 = upEvent(event3Time, event4Time, 21f, 31f);
+ mTouchState.update(event4, mConfig);
+ assertTap(mTouchState, 21f, 31f, 21f, 31f,
+ MultiTapStatus.DOUBLE_TAP, true);
+
+ // Generate a third ACTION_DOWN event whose time is within the double-tap timeout.
+ long event5Time = 1004;
+ MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f);
+ mTouchState.update(event5, mConfig);
+ assertTap(mTouchState, 22f, 32f, 21f, 31f,
+ MultiTapStatus.FIRST_TAP, false);
+ }
+
+ private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
+ return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+ }
+
+ private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) {
+ return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+ }
+
+ private static void assertSingleTap(EditorTouchState touchState, float lastDownX,
+ float lastDownY, float lastUpX, float lastUpY) {
+ assertThat(touchState.getLastDownX(), is(lastDownX));
+ assertThat(touchState.getLastDownY(), is(lastDownY));
+ assertThat(touchState.getLastUpX(), is(lastUpX));
+ assertThat(touchState.getLastUpY(), is(lastUpY));
+ assertThat(touchState.isDoubleTap(), is(false));
+ assertThat(touchState.isTripleClick(), is(false));
+ assertThat(touchState.isMultiTap(), is(false));
+ assertThat(touchState.isMultiTapInSameArea(), is(false));
+ }
+
+ private static void assertTap(EditorTouchState touchState,
+ float lastDownX, float lastDownY, float lastUpX, float lastUpY,
+ @MultiTapStatus int multiTapStatus, boolean isMultiTapInSameArea) {
+ assertThat(touchState.getLastDownX(), is(lastDownX));
+ assertThat(touchState.getLastDownY(), is(lastDownY));
+ assertThat(touchState.getLastUpX(), is(lastUpX));
+ assertThat(touchState.getLastUpY(), is(lastUpY));
+ assertThat(touchState.isDoubleTap(), is(multiTapStatus == MultiTapStatus.DOUBLE_TAP));
+ assertThat(touchState.isTripleClick(), is(multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
+ assertThat(touchState.isMultiTap(), is(multiTapStatus == MultiTapStatus.DOUBLE_TAP
+ || multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
+ assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
+ }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index c49d663..ff521be 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -104,7 +104,7 @@
prebuilt_etc {
name: "privapp_whitelist_com.android.settings",
- product_specific: true,
+ system_ext_specific: true,
sub_dir: "permissions",
src: "com.android.settings.xml",
filename_from_src: true,
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index cffb1ba..3fe421e 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -49,6 +49,8 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.emergency.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.provision.xml)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.provision.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.settings.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.settings.xml)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
# ******************************************************************
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f2a6452..cf3f51d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -339,8 +339,10 @@
<permission name="android.permission.SYSTEM_CAMERA" />
<!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
<permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
- <!-- Permission required for UiModeManager cts test. -->
+ <!-- Permission required for UiModeManager CTS test. -->
<permission name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
+ <!-- Permission required for Telecom car mode CTS tests. -->
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/media/Android.bp b/media/Android.bp
index 022fa9b..1912930 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -45,8 +45,8 @@
filegroup {
name: "updatable-media-srcs",
srcs: [
- ":mediasession2-srcs",
":mediaparser-srcs",
+ ":mediasession2-srcs",
],
}
@@ -73,7 +73,8 @@
name: "mediaparser-srcs",
srcs: [
"apex/java/android/media/MediaParser.java"
- ]
+ ],
+ path: "apex/java"
}
metalava_updatable_media_args = " --error UnhiddenSystemApi " +
diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java
index c06e283..8824269 100644
--- a/media/apex/java/android/media/MediaParser.java
+++ b/media/apex/java/android/media/MediaParser.java
@@ -15,10 +15,47 @@
*/
package android.media;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.text.TextUtils;
import android.util.Pair;
+import android.util.SparseArray;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
+import com.google.android.exoplayer2.extractor.Extractor;
+import com.google.android.exoplayer2.extractor.ExtractorInput;
+import com.google.android.exoplayer2.extractor.ExtractorOutput;
+import com.google.android.exoplayer2.extractor.PositionHolder;
+import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints;
+import com.google.android.exoplayer2.extractor.TrackOutput;
+import com.google.android.exoplayer2.extractor.amr.AmrExtractor;
+import com.google.android.exoplayer2.extractor.flv.FlvExtractor;
+import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
+import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
+import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
+import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
+import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
+import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
+import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
+import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
+import com.google.android.exoplayer2.extractor.ts.PsExtractor;
+import com.google.android.exoplayer2.extractor.ts.TsExtractor;
+import com.google.android.exoplayer2.extractor.wav.WavExtractor;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DataSpec;
+import com.google.android.exoplayer2.upstream.TransferListener;
+import com.google.android.exoplayer2.util.ParsableByteArray;
+
+import java.io.EOFException;
import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* Parses media container formats and extracts contained media samples and metadata.
@@ -32,16 +69,93 @@
* <p>Users must implement the following to use this class.
*
* <ul>
- * <li>{@link Input}: Provides the media containers bytes to parse.
- * <li>{@link OutputCallback}: Provides a sink for all extracted data and metadata.
+ * <li>{@link InputReader}: Provides the media container's bytes to parse.
+ * <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata.
* </ul>
*
- * TODO: Add usage example here.
+ * <p>The following code snippet includes a usage example:
+ *
+ * <pre>
+ * MyOutputConsumer myOutputConsumer = new MyOutputConsumer();
+ * MyInputReader myInputReader = new MyInputReader("www.example.com");
+ * MediaParser mediaParser = MediaParser.create(myOutputConsumer);
+ *
+ * while (mediaParser.advance(myInputReader)) {}
+ *
+ * mediaParser.release();
+ * mediaParser = null;
+ * </pre>
+ *
+ * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation
+ * which extracts and publishes all video samples:
+ *
+ * <pre>
+ *
+ * class VideoOutputConsumer implements MediaParser.OutputConsumer {
+ *
+ * private static final int MAXIMUM_SAMPLE_SIZE = ...;
+ * private byte[] sampleDataBuffer = new byte[MAXIMUM_SAMPLE_SIZE];
+ * private int videoTrackIndex = -1;
+ * private int bytesWrittenCount = 0;
+ *
+ * \@Override
+ * public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) { \/* Do nothing. *\/ }
+ *
+ * \@Override
+ * public void onFormat(int i, @NonNull MediaFormat mediaFormat) {
+ * if (videoTrackIndex == -1 && mediaFormat
+ * .getString(MediaFormat.KEY_MIME, \/* defaultValue= *\/ "").startsWith("video/")) {
+ * videoTrackIndex = i;
+ * }
+ * }
+ *
+ * \@Override
+ * public void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+ * throws IOException, InterruptedException {
+ * int numberOfBytesToRead = (int) inputReader.getLength();
+ * if (videoTrackIndex != trackIndex) {
+ * // Discard contents.
+ * inputReader.read(\/* bytes= *\/ null, \/* offset= *\/ 0, numberOfBytesToRead);
+ * }
+ * int bytesRead = inputReader.read(sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
+ * bytesWrittenCount += bytesRead;
+ * }
+ *
+ * \@Override
+ * public void onSampleCompleted(
+ * int trackIndex,
+ * long timeUs,
+ * int flags,
+ * int size,
+ * int offset,
+ * \@Nullable CryptoInfo cryptoData) {
+ * if (videoTrackIndex != trackIndex) {
+ * return; // It's not the video track. Ignore.
+ * }
+ * byte[] sampleData = new byte[size];
+ * System.arraycopy(sampleDataBuffer, bytesWrittenCount - size - offset, sampleData, \/*
+ * destPos= *\/ 0, size);
+ * // Place trailing bytes at the start of the buffer.
+ * System.arraycopy(
+ * sampleDataBuffer,
+ * bytesWrittenCount - offset,
+ * sampleDataBuffer,
+ * \/* destPos= *\/ 0,
+ * \/* size= *\/ offset);
+ * publishSample(sampleData, timeUs, flags);
+ * }
+ * }
+ *
+ * </pre>
*/
-// @HiddenApi
public final class MediaParser {
- /** Maps seek positions to corresponding positions in the stream. */
+ /**
+ * Maps seek positions to {@link SeekPoint SeekPoints} in the stream.
+ *
+ * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
+ * playing media samples.
+ */
public interface SeekMap {
/** Returned by {@link #getDurationUs()} when the duration is unknown. */
@@ -62,13 +176,14 @@
* <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with
* timestamp equal to or smaller than {@code timeUs}.
*
- * <p>{@code getSeekPoints(timeUs).second} contains the earlies seek point for samples with
+ * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with
* timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code
* timeUs}, the returned pair will contain the same {@link SeekPoint} twice.
*
* @param timeUs A seek time in microseconds.
* @return The corresponding {@link SeekPoint SeekPoints}.
*/
+ @NonNull
Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs);
}
@@ -76,30 +191,30 @@
public static final class SeekPoint {
/** A {@link SeekPoint} whose time and byte offset are both set to 0. */
- public static final SeekPoint START = new SeekPoint(0, 0);
+ public static final @NonNull SeekPoint START = new SeekPoint(0, 0);
/** The time of the seek point, in microseconds. */
- public final long mTimeUs;
+ public final long timeUs;
/** The byte offset of the seek point. */
- public final long mPosition;
+ public final long position;
/**
* @param timeUs The time of the seek point, in microseconds.
* @param position The byte offset of the seek point.
*/
- public SeekPoint(long timeUs, long position) {
- this.mTimeUs = timeUs;
- this.mPosition = position;
+ private SeekPoint(long timeUs, long position) {
+ this.timeUs = timeUs;
+ this.position = position;
}
@Override
- public String toString() {
- return "[timeUs=" + mTimeUs + ", position=" + mPosition + "]";
+ public @NonNull String toString() {
+ return "[timeUs=" + timeUs + ", position=" + position + "]";
}
@Override
- public boolean equals(Object obj) {
+ public boolean equals(@Nullable Object obj) {
if (this == obj) {
return true;
}
@@ -107,25 +222,26 @@
return false;
}
SeekPoint other = (SeekPoint) obj;
- return mTimeUs == other.mTimeUs && mPosition == other.mPosition;
+ return timeUs == other.timeUs && position == other.position;
}
@Override
public int hashCode() {
- int result = (int) mTimeUs;
- result = 31 * result + (int) mPosition;
+ int result = (int) timeUs;
+ result = 31 * result + (int) position;
return result;
}
}
/** Provides input data to {@link MediaParser}. */
- public interface Input {
+ public interface InputReader {
/**
* Reads up to {@code readLength} bytes of data and stores them into {@code buffer},
* starting at index {@code offset}.
*
- * <p>The call will block until at least one byte of data has been read.
+ * <p>This method blocks until at least one byte is read, the end of input is detected, or
+ * an exception is thrown. The read position advances to the first unread byte.
*
* @param buffer The buffer into which the read data should be stored.
* @param offset The start offset into {@code buffer} at which data should be written.
@@ -134,7 +250,7 @@
* of the input has been reached.
* @throws java.io.IOException If an error occurs reading from the source.
*/
- int read(byte[] buffer, int offset, int readLength)
+ int read(@NonNull byte[] buffer, int offset, int readLength)
throws IOException, InterruptedException;
/** Returns the current read position (byte offset) in the stream. */
@@ -144,22 +260,39 @@
long getLength();
}
- /** Receives extracted media sample data and metadata from {@link MediaParser}. */
- public interface OutputCallback {
+ /** {@link InputReader} that allows setting the read position. */
+ public interface SeekableInputReader extends InputReader {
/**
- * Called when the number of tracks is defined.
+ * Sets the read position at the given {@code position}.
*
- * @param numberOfTracks The number of tracks in the stream.
+ * <p>{@link #advance} will immediately return after calling this method.
+ *
+ * @param position The position to seek to, in bytes.
*/
- void onTracksFound(int numberOfTracks);
+ void seekToPosition(long position);
+ }
+
+ /** Receives extracted media sample data and metadata from {@link MediaParser}. */
+ public interface OutputConsumer {
/**
* Called when a {@link SeekMap} has been extracted from the stream.
*
+ * <p>This method is called at least once before any samples are {@link #onSampleCompleted
+ * complete}. May be called multiple times after that in order to add {@link SeekPoint
+ * SeekPoints}.
+ *
* @param seekMap The extracted {@link SeekMap}.
*/
- void onSeekMap(SeekMap seekMap);
+ void onSeekMap(@NonNull SeekMap seekMap);
+
+ /**
+ * Called when the number of tracks is found.
+ *
+ * @param numberOfTracks The number of tracks in the stream.
+ */
+ void onTracksFound(int numberOfTracks);
/**
* Called when the {@link MediaFormat} of the track is extracted from the stream.
@@ -167,7 +300,7 @@
* @param trackIndex The index of the track for which the {@link MediaFormat} was found.
* @param format The extracted {@link MediaFormat}.
*/
- void onFormat(int trackIndex, MediaFormat format);
+ void onFormat(int trackIndex, @NonNull MediaFormat format);
/**
* Called to write sample data to the output.
@@ -176,16 +309,15 @@
* thrown {@link IOException} caused by reading from {@code input}.
*
* @param trackIndex The index of the track to which the sample data corresponds.
- * @param input The {@link Input} from which to read the data.
- * @return
+ * @param inputReader The {@link InputReader} from which to read the data.
*/
- int onSampleData(int trackIndex, Input input) throws IOException, InterruptedException;
+ void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+ throws IOException, InterruptedException;
/**
- * Defines the boundaries and metadata of an extracted sample.
+ * Called once all the data of a sample has been passed to {@link #onSampleData}.
*
- * <p>The corresponding sample data will have already been passed to the output via calls to
- * {@link #onSampleData}.
+ * <p>Also includes sample metadata, like presentation timestamp and flags.
*
* @param trackIndex The index of the track to which the sample corresponds.
* @param timeUs The media timestamp associated with the sample, in microseconds.
@@ -203,57 +335,22 @@
int flags,
int size,
int offset,
- MediaCodec.CryptoInfo cryptoData);
- }
-
- /**
- * Controls the behavior of extractors' implementations.
- *
- * <p>DESIGN NOTE: For setting flags like workarounds and special behaviors for adaptive
- * streaming.
- */
- public static final class Parameters {
-
- // TODO: Implement.
-
- }
-
- /** Holds the result of an {@link #advance} invocation. */
- public static final class ResultHolder {
-
- /** Creates a new instance with {@link #result} holding {@link #ADVANCE_RESULT_CONTINUE}. */
- public ResultHolder() {
- result = ADVANCE_RESULT_CONTINUE;
- }
-
- /**
- * May hold {@link #ADVANCE_RESULT_END_OF_INPUT}, {@link #ADVANCE_RESULT_CONTINUE}, {@link
- * #ADVANCE_RESULT_SEEK}.
- */
- public int result;
-
- /**
- * If {@link #result} holds {@link #ADVANCE_RESULT_SEEK}, holds the stream position required
- * from the passed {@link Input} to the next {@link #advance} call. If {@link #result} does
- * not hold {@link #ADVANCE_RESULT_SEEK}, the value of this variable is undefined and should
- * be ignored.
- */
- public long seekPosition;
+ @Nullable MediaCodec.CryptoInfo cryptoData);
}
/**
* Thrown if all extractors implementations provided to {@link #create} failed to sniff the
* input content.
*/
- // @HiddenApi
public static final class UnrecognizedInputFormatException extends IOException {
/**
* Creates a new instance which signals that the extractors with the given names failed to
* parse the input.
*/
- public static UnrecognizedInputFormatException createForExtractors(
- String... extractorNames) {
+ @NonNull
+ private static UnrecognizedInputFormatException createForExtractors(
+ @NonNull String... extractorNames) {
StringBuilder builder = new StringBuilder();
builder.append("None of the available extractors ( ");
builder.append(extractorNames[0]);
@@ -270,21 +367,9 @@
}
}
- // Public constants.
+ // Private constants.
- /**
- * Returned by {@link #advance} if the {@link Input} passed to the next {@link #advance} is
- * required to provide data continuing from the position in the stream reached by the returning
- * call.
- */
- public static final int ADVANCE_RESULT_CONTINUE = -1;
- /** Returned by {@link #advance} if the end of the {@link Input} was reached. */
- public static final int ADVANCE_RESULT_END_OF_INPUT = -2;
- /**
- * Returned by {@link #advance} when its next call expects a specific stream position, which
- * will be held by {@link ResultHolder#seekPosition}.
- */
- public static final int ADVANCE_RESULT_SEEK = -3;
+ private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
// Instance creation methods.
@@ -293,13 +378,15 @@
* instance will attempt extraction without sniffing the content.
*
* @param name The name of the extractor that will be associated with the created instance.
- * @param outputCallback The {@link OutputCallback} to which track data and samples are pushed.
- * @param parameters Parameters that control specific aspects of the behavior of the extractors.
+ * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed.
* @return A new instance.
+ * @throws IllegalArgumentException If an invalid name is provided.
*/
- public static MediaParser createByName(
- String name, OutputCallback outputCallback, Parameters parameters) {
- throw new UnsupportedOperationException();
+ public static @NonNull MediaParser createByName(
+ @NonNull String name, @NonNull OutputConsumer outputConsumer) {
+ String[] nameAsArray = new String[] {name};
+ assertValidNames(nameAsArray);
+ return new MediaParser(outputConsumer, /* sniff= */ false, name);
}
/**
@@ -307,30 +394,46 @@
* the first {@link #advance} call. Extractor implementations will sniff the content in order of
* appearance in {@code extractorNames}.
*
- * @param outputCallback The {@link OutputCallback} to track data and samples are obtained.
- * @param parameters Parameters that control specific aspects of the behavior of the extractors.
+ * @param outputConsumer The {@link OutputConsumer} to which extracted data is output.
* @param extractorNames The names of the extractors to sniff the content with. If empty, a
* default array of names is used.
* @return A new instance.
*/
- public static MediaParser create(
- OutputCallback outputCallback, Parameters parameters, String... extractorNames) {
- throw new UnsupportedOperationException();
+ public static @NonNull MediaParser create(
+ @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) {
+ assertValidNames(extractorNames);
+ if (extractorNames.length == 0) {
+ extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
+ }
+ return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames);
}
// Misc static methods.
/**
* Returns an immutable list with the names of the extractors that are suitable for container
- * formats with the given {@code mimeTypes}. If an empty string is passed, all available
- * extractors' names are returned.
+ * formats with the given {@link MediaFormat}.
*
- * <p>TODO: Replace string with media type object.
+ * <p>TODO: List which properties are taken into account. E.g. MimeType.
*/
- public static List<String> getExtractorNames(String mimeTypes) {
+ public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
throw new UnsupportedOperationException();
}
+ // Private fields.
+
+ private final OutputConsumer mOutputConsumer;
+ private final String[] mExtractorNamesPool;
+ private final PositionHolder mPositionHolder;
+ private final InputReadingDataSource mDataSource;
+ private final ExtractorInputAdapter mScratchExtractorInputAdapter;
+ private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
+ private String mExtractorName;
+ private Extractor mExtractor;
+ private ExtractorInput mExtractorInput;
+ private long mPendingSeekPosition;
+ private long mPendingSeekTimeUs;
+
// Public methods.
/**
@@ -344,8 +447,8 @@
* @return The name of the backing extractor implementation, or null if the backing extractor
* implementation has not yet been selected.
*/
- public String getExtractorName() {
- throw new UnsupportedOperationException();
+ public @Nullable String getExtractorName() {
+ return mExtractorName;
}
/**
@@ -357,26 +460,85 @@
* <p>If this instance was created using {@link #create}. the first call to this method will
* sniff the content with the extractors with the provided names.
*
- * @param input The {@link Input} from which to obtain the media container data.
- * @param resultHolder The {@link ResultHolder} into which the result of the operation will be
- * written.
+ * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
+ * container data.
+ * @return Whether there is any data left to extract. Returns false if the end of input has been
+ * reached.
* @throws UnrecognizedInputFormatException
*/
- public void advance(Input input, ResultHolder resultHolder)
+ public boolean advance(@NonNull SeekableInputReader seekableInputReader)
throws IOException, InterruptedException {
- throw new UnsupportedOperationException();
+ if (mExtractorInput == null) {
+ // TODO: For efficiency, the same implementation should be used, by providing a
+ // clearBuffers() method, or similar.
+ mExtractorInput =
+ new DefaultExtractorInput(
+ mDataSource,
+ seekableInputReader.getPosition(),
+ seekableInputReader.getLength());
+ }
+ mDataSource.mInputReader = seekableInputReader;
+
+ if (mExtractor == null) {
+ for (String extractorName : mExtractorNamesPool) {
+ Extractor extractor =
+ EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance();
+ try {
+ if (extractor.sniff(mExtractorInput)) {
+ mExtractorName = extractorName;
+ mExtractor = extractor;
+ mExtractor.init(new ExtractorOutputAdapter());
+ break;
+ }
+ } catch (EOFException e) {
+ // Do nothing.
+ } catch (IOException | InterruptedException e) {
+ throw new IllegalStateException(e);
+ } finally {
+ mExtractorInput.resetPeekPosition();
+ }
+ }
+ if (mExtractor == null) {
+ UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool);
+ }
+ return true;
+ }
+
+ if (isPendingSeek()) {
+ mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs);
+ removePendingSeek();
+ }
+
+ mPositionHolder.position = seekableInputReader.getPosition();
+ int result = mExtractor.read(mExtractorInput, mPositionHolder);
+ if (result == Extractor.RESULT_END_OF_INPUT) {
+ return false;
+ }
+ if (result == Extractor.RESULT_SEEK) {
+ mExtractorInput = null;
+ seekableInputReader.seekToPosition(mPositionHolder.position);
+ }
+ return true;
}
/**
* Seeks within the media container being extracted.
*
- * <p>Following a call to this method, the {@link Input} passed to the next invocation of {@link
- * #advance} must provide data starting from {@link SeekPoint#mPosition} in the stream.
+ * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
+ * OutputConsumer#onSeekMap(SeekMap)}.
+ *
+ * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
+ * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
*
* @param seekPoint The {@link SeekPoint} to seek to.
*/
- public void seek(SeekPoint seekPoint) {
- throw new UnsupportedOperationException();
+ public void seek(@NonNull SeekPoint seekPoint) {
+ if (mExtractor == null) {
+ mPendingSeekPosition = seekPoint.position;
+ mPendingSeekTimeUs = seekPoint.timeUs;
+ } else {
+ mExtractor.seek(seekPoint.position, seekPoint.timeUs);
+ }
}
/**
@@ -386,6 +548,359 @@
* invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing.
*/
public void release() {
- throw new UnsupportedOperationException();
+ mExtractorInput = null;
+ mExtractor = null;
+ }
+
+ // Private methods.
+
+ private MediaParser(
+ OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) {
+ mOutputConsumer = outputConsumer;
+ mExtractorNamesPool = extractorNamesPool;
+ if (!sniff) {
+ mExtractorName = extractorNamesPool[0];
+ mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+ }
+ mPositionHolder = new PositionHolder();
+ mDataSource = new InputReadingDataSource();
+ removePendingSeek();
+ mScratchExtractorInputAdapter = new ExtractorInputAdapter();
+ mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
+ }
+
+ private boolean isPendingSeek() {
+ return mPendingSeekPosition >= 0;
+ }
+
+ private void removePendingSeek() {
+ mPendingSeekPosition = -1;
+ mPendingSeekTimeUs = -1;
+ }
+
+ // Private classes.
+
+ private static final class InputReadingDataSource implements DataSource {
+
+ public InputReader mInputReader;
+
+ @Override
+ public void addTransferListener(TransferListener transferListener) {
+ // Do nothing.
+ }
+
+ @Override
+ public long open(DataSpec dataSpec) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int readLength) throws IOException {
+ // TODO: Reevaluate interruption in Input.
+ try {
+ return mInputReader.read(buffer, offset, readLength);
+ } catch (InterruptedException e) {
+ // TODO: Remove.
+ throw new RuntimeException();
+ }
+ }
+
+ @Override
+ public Uri getUri() {
+ return null;
+ }
+
+ @Override
+ public Map<String, List<String>> getResponseHeaders() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private final class ExtractorOutputAdapter implements ExtractorOutput {
+
+ private final SparseArray<TrackOutput> mTrackOutputAdapters;
+ private boolean mTracksEnded;
+
+ private ExtractorOutputAdapter() {
+ mTrackOutputAdapters = new SparseArray<>();
+ }
+
+ @Override
+ public TrackOutput track(int id, int type) {
+ TrackOutput trackOutput = mTrackOutputAdapters.get(id);
+ if (trackOutput == null) {
+ trackOutput = new TrackOutputAdapter(mTrackOutputAdapters.size());
+ mTrackOutputAdapters.put(id, trackOutput);
+ }
+ return trackOutput;
+ }
+
+ @Override
+ public void endTracks() {
+ mOutputConsumer.onTracksFound(mTrackOutputAdapters.size());
+ }
+
+ @Override
+ public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+ mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap));
+ }
+ }
+
+ private class TrackOutputAdapter implements TrackOutput {
+
+ private final int mTrackIndex;
+
+ private TrackOutputAdapter(int trackIndex) {
+ mTrackIndex = trackIndex;
+ }
+
+ @Override
+ public void format(Format format) {
+ mOutputConsumer.onFormat(mTrackIndex, toMediaFormat(format));
+ }
+
+ @Override
+ public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
+ throws IOException, InterruptedException {
+ mScratchExtractorInputAdapter.setExtractorInput(input, length);
+ long positionBeforeReading = mScratchExtractorInputAdapter.getPosition();
+ mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter);
+ return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading);
+ }
+
+ @Override
+ public void sampleData(ParsableByteArray data, int length) {
+ mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
+ try {
+ mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter);
+ } catch (IOException | InterruptedException e) {
+ // Unexpected.
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void sampleMetadata(
+ long timeUs, int flags, int size, int offset, CryptoData encryptionData) {
+ mOutputConsumer.onSampleCompleted(
+ mTrackIndex, timeUs, flags, size, offset, toCryptoInfo(encryptionData));
+ }
+ }
+
+ private static final class ExtractorInputAdapter implements InputReader {
+
+ private ExtractorInput mExtractorInput;
+ private int mCurrentPosition;
+ private long mLength;
+
+ public void setExtractorInput(ExtractorInput extractorInput, long length) {
+ mExtractorInput = extractorInput;
+ mCurrentPosition = 0;
+ mLength = length;
+ }
+
+ // Input implementation.
+
+ @Override
+ public int read(byte[] buffer, int offset, int readLength)
+ throws IOException, InterruptedException {
+ int readBytes = mExtractorInput.read(buffer, offset, readLength);
+ mCurrentPosition += readBytes;
+ return readBytes;
+ }
+
+ @Override
+ public long getPosition() {
+ return mCurrentPosition;
+ }
+
+ @Override
+ public long getLength() {
+ return mLength - mCurrentPosition;
+ }
+ }
+
+ private static final class ParsableByteArrayAdapter implements InputReader {
+
+ private ParsableByteArray mByteArray;
+ private long mLength;
+ private int mCurrentPosition;
+
+ public void resetWithByteArray(ParsableByteArray byteArray, long length) {
+ mByteArray = byteArray;
+ mCurrentPosition = 0;
+ mLength = length;
+ }
+
+ // Input implementation.
+
+ @Override
+ public int read(byte[] buffer, int offset, int readLength) {
+ mByteArray.readBytes(buffer, offset, readLength);
+ mCurrentPosition += readLength;
+ return readLength;
+ }
+
+ @Override
+ public long getPosition() {
+ return mCurrentPosition;
+ }
+
+ @Override
+ public long getLength() {
+ return mLength - mCurrentPosition;
+ }
+ }
+
+ /** Creates extractor instances. */
+ private interface ExtractorFactory {
+
+ /** Returns a new extractor instance. */
+ Extractor createInstance();
+ }
+
+ private static class ExoToMediaParserSeekMapAdapter implements SeekMap {
+
+ private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
+
+ private ExoToMediaParserSeekMapAdapter(
+ com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+ mExoPlayerSeekMap = exoplayerSeekMap;
+ }
+
+ @Override
+ public boolean isSeekable() {
+ return mExoPlayerSeekMap.isSeekable();
+ }
+
+ @Override
+ public long getDurationUs() {
+ return mExoPlayerSeekMap.getDurationUs();
+ }
+
+ @Override
+ public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
+ SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+ return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
+ }
+ }
+
+ // Private static methods.
+
+ private static MediaFormat toMediaFormat(Format format) {
+
+ // TODO: Add if (value != Format.NO_VALUE);
+
+ MediaFormat result = new MediaFormat();
+ result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
+ result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+ if (format.colorInfo != null) {
+ result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
+ result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
+ result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace);
+ if (format.colorInfo.hdrStaticInfo != null) {
+ result.setByteBuffer(
+ MediaFormat.KEY_HDR_STATIC_INFO,
+ ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
+ }
+ }
+ result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
+ result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
+ result.setInteger(MediaFormat.KEY_WIDTH, format.width);
+ result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+ List<byte[]> initData = format.initializationData;
+ if (initData != null) {
+ for (int i = 0; i < initData.size(); i++) {
+ result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
+ }
+ }
+ result.setString(MediaFormat.KEY_LANGUAGE, format.language);
+ result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+ result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+ result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
+ result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+
+ int selectionFlags = format.selectionFlags;
+ // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the
+ // extractor.
+ if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
+ result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
+ }
+ if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) {
+ result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
+ }
+ if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
+ result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
+ }
+
+ // LACK OF SUPPORT FOR:
+ // format.accessibilityChannel;
+ // format.codecs;
+ // format.containerMimeType;
+ // format.drmInitData;
+ // format.encoderDelay;
+ // format.encoderPadding;
+ // format.id;
+ // format.metadata;
+ // format.pixelWidthHeightRatio;
+ // format.roleFlags;
+ // format.stereoMode;
+ // format.subsampleOffsetUs;
+ return result;
+ }
+
+ private static int toFrameworkFlags(int flags) {
+ // TODO: Implement.
+ return 0;
+ }
+
+ private static MediaCodec.CryptoInfo toCryptoInfo(TrackOutput.CryptoData encryptionData) {
+ // TODO: Implement.
+ return null;
+ }
+
+ /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
+ private static SeekPoint toSeekPoint(
+ com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
+ return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position);
+ }
+
+ private static void assertValidNames(@NonNull String[] names) {
+ for (String name : names) {
+ if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) {
+ throw new IllegalArgumentException(
+ "Invalid extractor name: "
+ + name
+ + ". Supported extractors are: "
+ + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet())
+ + ".");
+ }
+ }
+ }
+
+ // Static initialization.
+
+ static {
+ // Using a LinkedHashMap to keep the insertion order when iterating over the keys.
+ LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
+ extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new);
+ extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new);
+ extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new);
+ extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new);
+ extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new);
+ extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new);
+ extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new);
+ extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new);
+ extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new);
+ extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new);
+ extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new);
+ extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new);
+ extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new);
+ EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
}
}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 63657a6..6523e30 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -36,9 +36,12 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@@ -203,6 +206,16 @@
securityLevel);
}
+ /**
+ * @return list of crypto schemes (as {@link UUID}s) for which
+ * {@link #isCryptoSchemeSupported(UUID)} returns true; each {@link UUID}
+ * can be used as input to create {@link MediaDrm} objects via {@link #MediaDrm(UUID)}.
+ */
+ public static final @NonNull List<UUID> getSupportedCryptoSchemes(){
+ byte[] uuidBytes = getSupportedCryptoSchemesNative();
+ return getUUIDsFromByteArray(uuidBytes);
+ }
+
private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
@@ -216,6 +229,28 @@
return uuidBytes;
}
+ private static final UUID getUUIDFromByteArray(@NonNull byte[] uuidBytes, int off) {
+ long msb = 0;
+ long lsb = 0;
+
+ for (int i = 0; i < 8; ++i) {
+ msb = (msb << 8) | (0xffl & uuidBytes[off + i]);
+ lsb = (lsb << 8) | (0xffl & uuidBytes[off + i + 8]);
+ }
+
+ return new UUID(msb, lsb);
+ }
+
+ private static final List<UUID> getUUIDsFromByteArray(@NonNull byte[] uuidBytes) {
+ Set<UUID> uuids = new LinkedHashSet<>();
+ for (int off = 0; off < uuidBytes.length; off+=16) {
+ uuids.add(getUUIDFromByteArray(uuidBytes, off));
+ }
+ return new ArrayList<>(uuids);
+ }
+
+ private static final native byte[] getSupportedCryptoSchemesNative();
+
private static final native boolean isCryptoSchemeSupportedNative(
@NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 064ac75..12b3e67 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -59,6 +59,7 @@
"libsonivox",
"android.hardware.cas@1.0",
"android.hardware.cas.native@1.0",
+ "android.hardware.drm@1.3",
"android.hidl.memory@1.0",
"android.hidl.token@1.0-utils",
],
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index acda18e..f38a29c 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -27,6 +27,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
+#include <android/hardware/drm/1.3/IDrmFactory.h>
#include <binder/Parcel.h>
#include <binder/PersistableBundle.h>
#include <cutils/properties.h>
@@ -38,7 +39,7 @@
#include <mediadrm/IDrm.h>
using ::android::os::PersistableBundle;
-
+namespace drm = ::android::hardware::drm;
namespace android {
@@ -971,6 +972,26 @@
return level;
}
+static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
+ std::vector<uint8_t> bv;
+ for (auto &factory : DrmUtils::MakeDrmFactories()) {
+ sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
+ if (factoryV1_3 == nullptr) {
+ continue;
+ }
+ factoryV1_3->getSupportedCryptoSchemes(
+ [&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes) {
+ for (const auto &scheme : schemes) {
+ bv.insert(bv.end(), scheme.data(), scheme.data() + scheme.size());
+ }
+ });
+ }
+
+ jbyteArray jUuidBytes = env->NewByteArray(bv.size());
+ env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
+ return jUuidBytes;
+}
+
static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType,
jint jSecurityLevel) {
@@ -1941,6 +1962,9 @@
{ "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
(void *)android_media_MediaDrm_native_setup },
+ { "getSupportedCryptoSchemesNative", "()[B",
+ (void *)android_media_MediaDrm_getSupportedCryptoSchemesNative },
+
{ "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
(void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index bcb76d7..c8532e0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -119,11 +119,11 @@
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -140,6 +140,7 @@
import java.util.Optional;
import javax.inject.Named;
+import javax.inject.Provider;
import dagger.Lazy;
@@ -156,7 +157,6 @@
private static final float FLING_SPEED_UP_FACTOR = 0.6f;
private final ScrimController mScrimController;
- private final StatusBarWindowViewController mStatusBarWindowViewController;
private final LockscreenLockIconController mLockscreenLockIconController;
private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
@@ -174,7 +174,6 @@
private final Object mQueueLock = new Object();
private final CarNavigationBarController mCarNavigationBarController;
- private final Lazy<DrivingStateHelper> mDrivingStateHelperLazy;
private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
private final CarServiceProvider mCarServiceProvider;
@@ -289,7 +288,6 @@
NotificationListener notificationListener,
ConfigurationController configurationController,
StatusBarWindowController statusBarWindowController,
- StatusBarWindowViewController statusBarWindowViewController,
LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@@ -302,6 +300,7 @@
VolumeComponent volumeComponent,
CommandQueue commandQueue,
Optional<Recents> recents,
+ Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
@@ -314,7 +313,6 @@
DismissCallbackRegistry dismissCallbackRegistry,
/* Car Settings injected components. */
CarServiceProvider carServiceProvider,
- Lazy<DrivingStateHelper> drivingStateHelperLazy,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
CarNavigationBarController carNavigationBarController) {
@@ -368,7 +366,6 @@
notificationListener,
configurationController,
statusBarWindowController,
- statusBarWindowViewController,
lockscreenLockIconController,
dozeParameters,
scrimController,
@@ -382,6 +379,7 @@
volumeComponent,
commandQueue,
recents,
+ statusBarComponentBuilder,
pluginManager,
remoteInputUriController,
dividerOptional,
@@ -392,11 +390,9 @@
viewMediatorCallback,
dismissCallbackRegistry);
mScrimController = scrimController;
- mStatusBarWindowViewController = statusBarWindowViewController;
mLockscreenLockIconController = lockscreenLockIconController;
mDeviceProvisionedController = deviceProvisionedController;
mCarServiceProvider = carServiceProvider;
- mDrivingStateHelperLazy = drivingStateHelperLazy;
mPowerManagerHelperLazy = powerManagerHelperLazy;
mFullscreenUserSwitcherLazy = fullscreenUserSwitcherLazy;
mCarNavigationBarController = carNavigationBarController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 6529868..eff60fa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -78,11 +78,11 @@
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -96,6 +96,7 @@
import java.util.Optional;
import javax.inject.Named;
+import javax.inject.Provider;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -162,7 +163,6 @@
NotificationListener notificationListener,
ConfigurationController configurationController,
StatusBarWindowController statusBarWindowController,
- StatusBarWindowViewController statusBarWindowViewController,
LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@@ -175,6 +175,7 @@
VolumeComponent volumeComponent,
CommandQueue commandQueue,
Optional<Recents> recentsOptional,
+ Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
@@ -186,7 +187,6 @@
ViewMediatorCallback viewMediatorCallback,
DismissCallbackRegistry dismissCallbackRegistry,
CarServiceProvider carServiceProvider,
- Lazy<DrivingStateHelper> drivingStateHelperLazy,
Lazy<PowerManagerHelper> powerManagerHelperLazy,
Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
CarNavigationBarController carNavigationBarController) {
@@ -240,7 +240,6 @@
notificationListener,
configurationController,
statusBarWindowController,
- statusBarWindowViewController,
lockscreenLockIconController,
dozeParameters,
scrimController,
@@ -253,6 +252,7 @@
volumeComponent,
commandQueue,
recentsOptional,
+ statusBarComponentBuilder,
pluginManager,
remoteInputUriController,
dividerOptional,
@@ -263,7 +263,6 @@
viewMediatorCallback,
dismissCallbackRegistry,
carServiceProvider,
- drivingStateHelperLazy,
powerManagerHelperLazy,
fullscreenUserSwitcherLazy,
carNavigationBarController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
deleted file mode 100644
index 60934ab..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.car.Car;
-import android.car.drivingstate.CarDrivingStateEvent;
-import android.car.drivingstate.CarDrivingStateManager;
-import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.car.CarServiceProvider;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state
- * changes.
- */
-@Singleton
-public class DrivingStateHelper {
- public static final String TAG = "DrivingStateHelper";
-
- private final CarServiceProvider mCarServiceProvider;
-
- private CarDrivingStateManager mDrivingStateManager;
- private CarDrivingStateEventListener mDrivingStateHandler;
-
- @Inject
- public DrivingStateHelper(CarServiceProvider carServiceProvider) {
- mCarServiceProvider = carServiceProvider;
- }
-
- /**
- * Sets the {@link CarDrivingStateEventListener}. Should be set before calling {@link
- * #connectToCarService()}.
- */
- public void setCarDrivingStateEventListener(
- @NonNull CarDrivingStateEventListener carDrivingStateEventListener) {
- mDrivingStateHandler = carDrivingStateEventListener;
- }
-
- /**
- * Queries {@link CarDrivingStateManager} for current driving state. Returns {@code true} if car
- * is idling or moving, {@code false} otherwise.
- */
- public boolean isCurrentlyDriving() {
- if (mDrivingStateManager == null) {
- return false;
- }
- CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
- if (currentState != null) {
- return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
- || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
- }
- return false; // Default to false.
- }
-
- /**
- * Establishes connection with the Car service.
- */
- public void connectToCarService() {
- mCarServiceProvider.addListener(mCarServiceLifecycleListener);
- }
-
- private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener =
- car -> {
- logD("Car Service connected");
- mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
- Car.CAR_DRIVING_STATE_SERVICE);
- if (mDrivingStateManager != null) {
- mDrivingStateManager.registerListener(mDrivingStateHandler);
- mDrivingStateHandler.onDrivingStateChanged(
- mDrivingStateManager.getCurrentCarDrivingState());
- } else {
- Log.e(TAG, "CarDrivingStateService service not available");
- }
- };
-
- private void logD(String message) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, message);
- }
- }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 7500bcd..0a5f80f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -21,6 +21,7 @@
import static android.os.UserManager.DISALLOW_ADD_USER;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
@@ -266,7 +267,10 @@
if (userRecord.mIsStartGuestSession) {
notifyUserSelected(userRecord);
- mCarUserManagerHelper.startGuestSession(mGuestName);
+ UserInfo guest = createNewOrFindExistingGuest(mContext);
+ if (guest != null) {
+ mCarUserManagerHelper.switchToUser(guest);
+ }
return;
}
@@ -381,6 +385,24 @@
return circleIcon;
}
+ /**
+ * Finds the existing Guest user, or creates one if it doesn't exist.
+ * @param context App context
+ * @return UserInfo representing the Guest user
+ */
+ @Nullable
+ public UserInfo createNewOrFindExistingGuest(Context context) {
+ // CreateGuest will return null if a guest already exists.
+ UserInfo newGuest = mUserManager.createGuest(context, mGuestName);
+ if (newGuest != null) {
+ new UserIconProvider().assignDefaultIcon(
+ mUserManager, context.getResources(), newGuest);
+ return newGuest;
+ }
+
+ return mUserManager.findCurrentGuestUser();
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == BUTTON_POSITIVE) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
index 9464eab..9018290 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
@@ -88,7 +88,7 @@
* @param userInfo User whose avatar is set to default icon.
* @return Bitmap of the user icon.
*/
- private Bitmap assignDefaultIcon(
+ public Bitmap assignDefaultIcon(
UserManager userManager, Resources resources, UserInfo userInfo) {
Bitmap bitmap = userInfo.isGuest()
? getGuestUserDefaultIcon(resources)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 785dd56..96aee51 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -44,8 +44,8 @@
private final CachedBluetoothDeviceManager mDeviceManager;
static final ParcelUuid[] SINK_UUIDS = {
- BluetoothUuid.AudioSink,
- BluetoothUuid.AdvAudioDist,
+ BluetoothUuid.A2DP_SINK,
+ BluetoothUuid.ADV_AUDIO_DIST,
};
static final String NAME = "A2DP";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 4ce9d3e..55765dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -40,8 +40,8 @@
private final CachedBluetoothDeviceManager mDeviceManager;
static final ParcelUuid[] SRC_UUIDS = {
- BluetoothUuid.AudioSource,
- BluetoothUuid.AdvAudioDist,
+ BluetoothUuid.A2DP_SOURCE,
+ BluetoothUuid.ADV_AUDIO_DIST,
};
static final String NAME = "A2DPSink";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
index 8dec86a..b8ad321 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
@@ -22,6 +22,8 @@
import android.os.ParcelUuid;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
/**
* BluetoothDeviceFilter contains a static method that returns a
* Filter object that returns whether or not the BluetoothDevice
@@ -130,7 +132,7 @@
@Override
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
if (uuids != null) {
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH)) {
return true;
}
}
@@ -144,7 +146,7 @@
@Override
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
if (uuids != null) {
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.PANU)) {
return true;
}
}
@@ -158,7 +160,7 @@
@Override
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
if (uuids != null) {
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.NAP)) {
return true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 833c4ac..0666596 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,6 +35,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.settingslib.R;
import com.android.settingslib.Utils;
@@ -685,9 +686,9 @@
ParcelUuid[] uuids = mDevice.getUuids();
long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) {
timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
- } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+ } else if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID)) {
timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index c1933fd..9f7b718 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -45,7 +45,7 @@
static final ParcelUuid[] UUIDS = {
BluetoothUuid.HSP,
- BluetoothUuid.Handsfree,
+ BluetoothUuid.HFP,
};
static final String NAME = "HEADSET";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4bdbc31..860b77d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -44,7 +44,7 @@
static final ParcelUuid[] SRC_UUIDS = {
BluetoothUuid.HSP_AG,
- BluetoothUuid.Handsfree_AG,
+ BluetoothUuid.HFP_AG,
};
static final String NAME = "HEADSET_CLIENT";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 29c6d71..ae2acbe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -30,8 +30,8 @@
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbap;
import android.bluetooth.BluetoothPbapClient;
-import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +40,7 @@
import androidx.annotation.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import java.util.ArrayList;
@@ -471,43 +472,40 @@
}
if (mHeadsetProfile != null) {
- if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
- (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
+ if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG)
+ && ArrayUtils.contains(uuids, BluetoothUuid.HSP))
+ || (ArrayUtils.contains(localUuids, BluetoothUuid.HFP_AG)
+ && ArrayUtils.contains(uuids, BluetoothUuid.HFP))) {
profiles.add(mHeadsetProfile);
removedProfiles.remove(mHeadsetProfile);
}
}
if ((mHfpClientProfile != null) &&
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
- BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) {
+ ArrayUtils.contains(uuids, BluetoothUuid.HFP_AG)
+ && ArrayUtils.contains(localUuids, BluetoothUuid.HFP)) {
profiles.add(mHfpClientProfile);
removedProfiles.remove(mHfpClientProfile);
}
- if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
- mA2dpProfile != null) {
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) && mA2dpProfile != null) {
profiles.add(mA2dpProfile);
removedProfiles.remove(mA2dpProfile);
}
- if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
- mA2dpSinkProfile != null) {
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS)
+ && mA2dpSinkProfile != null) {
profiles.add(mA2dpSinkProfile);
removedProfiles.remove(mA2dpSinkProfile);
}
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
- mOppProfile != null) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH) && mOppProfile != null) {
profiles.add(mOppProfile);
removedProfiles.remove(mOppProfile);
}
- if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
- BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
- mHidProfile != null) {
+ if ((ArrayUtils.contains(uuids, BluetoothUuid.HID)
+ || ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) && mHidProfile != null) {
profiles.add(mHidProfile);
removedProfiles.remove(mHidProfile);
}
@@ -520,8 +518,8 @@
if(isPanNapConnected)
if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
- if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
- mPanProfile != null) || isPanNapConnected) {
+ if ((ArrayUtils.contains(uuids, BluetoothUuid.NAP) && mPanProfile != null)
+ || isPanNapConnected) {
profiles.add(mPanProfile);
removedProfiles.remove(mPanProfile);
}
@@ -545,20 +543,18 @@
removedProfiles.remove(mMapClientProfile);
}
- if ((mPbapClientProfile != null) &&
- BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.PBAP_PCE) &&
- BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
+ if ((mPbapClientProfile != null) && ArrayUtils.contains(localUuids, BluetoothUuid.PBAP_PCE)
+ && BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
}
- if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
- mHearingAidProfile != null) {
+ if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID) && mHearingAidProfile != null) {
profiles.add(mHearingAidProfile);
removedProfiles.remove(mHearingAidProfile);
}
- if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+ if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
profiles.add(mSapProfile);
removedProfiles.remove(mSapProfile);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 17104e4..d91226e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -46,7 +46,7 @@
// The UUIDs indicate that remote device might access pbap server
static final ParcelUuid[] PBAB_CLIENT_UUIDS = {
BluetoothUuid.HSP,
- BluetoothUuid.Handsfree,
+ BluetoothUuid.HFP,
BluetoothUuid.PBAP_PCE
};
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 5d5872e..fd5b053 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -132,7 +132,7 @@
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HID_HOST}));
mProfileManager.updateLocalProfiles();
- ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.Hid};
+ ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.HID};
ParcelUuid[] localUuids = new ParcelUuid[]{};
List<LocalBluetoothProfile> profiles = new ArrayList<>();
List<LocalBluetoothProfile> removedProfiles = new ArrayList<>();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 1527de1..1f68742 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -70,5 +70,7 @@
Settings.Global.CHARGING_VIBRATION_ENABLED,
Settings.Global.AWARE_ALLOWED,
Settings.Global.NOTIFICATION_BUBBLES,
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4a0ed6f..3d278db 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -149,5 +149,7 @@
VALIDATORS.put(
Global.POWER_BUTTON_VERY_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Global.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR);
+ VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 540726b..c23a494 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -268,6 +268,7 @@
Settings.Global.ERROR_LOGCAT_PREFIX,
Settings.Global.EUICC_PROVISIONED,
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
+ Settings.Global.EUICC_UNSUPPORTED_COUNTRIES,
Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1a658f4..7f1d528 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -215,9 +215,12 @@
<!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
- <!-- Permission requried for CTS test - UiModeManagerTest -->
+ <!-- Permission required for CTS test - UiModeManagerTest -->
<uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
+ <!-- Permission required for CTS test - CarModeInCallServiceTest -->
+ <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0a671d9..6f68038 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -288,6 +288,11 @@
android:exported="false"
android:permission="com.android.systemui.permission.SELF" />
+ <service android:name=".assist.AssistHandleService"
+ android:exported="true"
+ android:enabled="false"
+ />
+
<!-- started from PhoneWindowManager
TODO: Should have an android:permission attribute -->
<service android:name=".screenshot.TakeScreenshotService"
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 1fe6141..95716c8 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -36,5 +36,6 @@
<string-array name="audio_recording_disclosure_exempt_apps" translatable="false">
<item>com.google.android.katniss</item>
+ <item>com.google.android.apps.mediashell</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index c1a23c8..41dd5bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -37,7 +37,7 @@
*/
@Singleton
public class ForegroundServiceController {
- private static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
+ public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
@@ -139,6 +139,8 @@
}
}
+ // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+ // ForegroundCoordinator
// Update appOp if there's an associated pending or visible notification:
final String foregroundKey = getStandardLayoutKey(userId, packageName);
if (foregroundKey != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index b983966..8105faa 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -27,6 +27,8 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import javax.inject.Inject;
@@ -46,9 +48,13 @@
@Inject
public ForegroundServiceNotificationListener(Context context,
ForegroundServiceController foregroundServiceController,
- NotificationEntryManager notificationEntryManager) {
+ NotificationEntryManager notificationEntryManager,
+ NotifCollection notifCollection) {
mContext = context;
mForegroundServiceController = foregroundServiceController;
+
+ // TODO: (b/145659174) remove mEntryManager when moving to NewNotifPipeline. Replaced by
+ // ForegroundCoordinator
mEntryManager = notificationEntryManager;
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
@@ -69,8 +75,24 @@
removeNotification(entry.getSbn());
}
});
-
mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
+
+ notifCollection.addCollectionListener(new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ addNotification(entry, entry.getImportance());
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ updateNotification(entry, entry.getImportance());
+ }
+
+ @Override
+ public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+ removeNotification(entry.getSbn());
+ }
+ });
}
/**
@@ -152,6 +174,8 @@
true /* create if not found */);
}
+ // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+ // ForegroundCoordinator
private void tagForeground(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
index a8ae654..2ef46dc 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
@@ -24,7 +24,7 @@
/**
* Struct to track relevant packages and notifications for a userid's foreground services.
*/
-class ForegroundServicesUserState {
+public class ForegroundServicesUserState {
// shelf life of foreground services before they go bad
private static final long FG_SERVICE_GRACE_MILLIS = 5000;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
new file mode 100644
index 0000000..9ceafc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
@@ -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 com.android.systemui.assist
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import dagger.Lazy
+import javax.inject.Inject
+
+class AssistHandleService @Inject constructor(private val assistManager: Lazy<AssistManager>)
+ : Service() {
+
+ private val binder = object : IAssistHandleService.Stub() {
+ override fun requestAssistHandles() {
+ assistManager.get().requestAssistHandles()
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return binder
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 6f5a17d..96939b01 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.assist;
+import android.app.Service;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
@@ -33,8 +34,11 @@
import javax.inject.Named;
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
/** Module for dagger injections related to the Assistant. */
@Module
@@ -87,4 +91,9 @@
static Clock provideSystemClock() {
return SystemClock::uptimeMillis;
}
+
+ @Binds
+ @IntoMap
+ @ClassKey(AssistHandleService.class)
+ abstract Service bindAssistHandleService(AssistHandleService assistHandleService);
}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
similarity index 63%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
index 007ec94..ef07d9d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2014, The Android Open Source Project
+ * Copyright (c) 2009, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.net.wifi;
+package com.android.systemui.assist;
-parcelable WifiActivityEnergyInfo;
+/** Interface implemented by AssisthandleService and called by on-device intelligence. */
+interface IAssistHandleService {
+
+ /** Request that the Assistant Handles be shown. */
+ oneway void requestAssistHandles();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ef2868b..f3a7ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -199,7 +199,7 @@
mExpandedView = (BubbleExpandedView) inflater.inflate(
R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
- mExpandedView.setBubble(this, stackView, mAppName);
+ mExpandedView.setBubble(this, stackView);
mInflated = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1025321..2966aef 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -694,10 +694,11 @@
@Override
public void onPendingEntryAdded(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
+ boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+ mContext, entry, previouslyUserCreated);
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
- && canLaunchInActivityView(mContext, entry)) {
+ && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
updateBubble(entry);
}
}
@@ -705,10 +706,11 @@
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
- BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
+ boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+ mContext, entry, previouslyUserCreated);
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
- && canLaunchInActivityView(mContext, entry);
+ && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
@@ -1016,11 +1018,6 @@
PendingIntent intent = entry.getBubbleMetadata() != null
? entry.getBubbleMetadata().getIntent()
: null;
- return canLaunchIntentInActivityView(context, entry, intent);
- }
-
- static boolean canLaunchIntentInActivityView(Context context, NotificationEntry entry,
- PendingIntent intent) {
if (intent == null) {
Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index a69ff00..efc955d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -31,15 +31,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.os.RemoteException;
import android.service.notification.StatusBarNotification;
@@ -102,9 +99,7 @@
private int mExpandedViewTouchSlop;
private Bubble mBubble;
- private PackageManager mPm;
private String mAppName;
- private Drawable mAppIcon;
private BubbleController mBubbleController = Dependency.get(BubbleController.class);
private WindowManager mWindowManager;
@@ -212,7 +207,6 @@
public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mPm = context.getPackageManager();
mDisplaySize = new Point();
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
// Get the real size -- this includes screen decorations (notches, statusbar, navbar).
@@ -350,29 +344,14 @@
/**
* Sets the bubble used to populate this view.
*/
- public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
+ public void setBubble(Bubble bubble, BubbleStackView stackView) {
if (DEBUG_BUBBLE_EXPANDED_VIEW) {
Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
}
-
mStackView = stackView;
mBubble = bubble;
- mAppName = appName;
+ mAppName = bubble.getAppName();
- try {
- ApplicationInfo info = mPm.getApplicationInfo(
- bubble.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- mAppIcon = mPm.getApplicationIcon(info);
- } catch (PackageManager.NameNotFoundException e) {
- // Do nothing.
- }
- if (mAppIcon == null) {
- mAppIcon = mPm.getDefaultActivityIcon();
- }
applyThemeAttrs();
showSettingsIcon();
updateExpandedView();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 17d6737..fd7fff4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -21,7 +21,6 @@
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
-import static com.android.systemui.bubbles.BubbleController.canLaunchIntentInActivityView;
import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -62,7 +61,7 @@
private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
- private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
+ private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true;
private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
@@ -107,8 +106,10 @@
* If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
* {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
* the notification has necessary info for BubbleMetadata.
+ *
+ * @return whether an adjustment was made.
*/
- static void adjustForExperiments(Context context, NotificationEntry entry,
+ static boolean adjustForExperiments(Context context, NotificationEntry entry,
boolean previouslyUserCreated) {
Notification.BubbleMetadata metadata = null;
boolean addedMetadata = false;
@@ -176,7 +177,9 @@
Log.d(TAG, "Setting FLAG_BUBBLE for: " + entry.getKey());
}
entry.setFlagBubble(true);
+ return true;
}
+ return addedMetadata;
}
static Notification.BubbleMetadata createFromNotif(Context context, NotificationEntry entry) {
@@ -193,7 +196,7 @@
? notification.getLargeIcon()
: notification.getSmallIcon();
}
- if (canLaunchIntentInActivityView(context, entry, intent)) {
+ if (intent != null) {
return new Notification.BubbleMetadata.Builder()
.setDesiredHeight(BUBBLE_HEIGHT)
.setIcon(icon)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 9bd729e..442313d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,7 +35,9 @@
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.time.SystemClock;
import com.android.systemui.util.time.SystemClockImpl;
@@ -52,7 +54,9 @@
* implementation.
*/
@Module(includes = {AssistModule.class,
- PeopleHubModule.class})
+ ConcurrencyModule.class,
+ PeopleHubModule.class},
+ subcomponents = {StatusBarComponent.class})
public abstract class SystemUIModule {
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e9265749..e50e0fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -25,7 +25,6 @@
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.InjectionInflationController;
@@ -73,12 +72,6 @@
Dependency.DependencyInjector createDependency();
/**
- * Injects the StatusBar.
- */
- @Singleton
- StatusBar.StatusBarInjector getStatusBarInjector();
-
- /**
* FragmentCreator generates all Fragments that need injection.
*/
@Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java
new file mode 100644
index 0000000..141c901
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java
@@ -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.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Background {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
new file mode 100644
index 0000000..7b09774
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
@@ -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.
+ */
+
+package com.android.systemui.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Main {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b5845947..63a7771 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -669,7 +669,10 @@
// Take an "interactive" bugreport.
MetricsLogger.action(mContext,
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- ActivityManager.getService().requestInteractiveBugReport();
+ if (!ActivityManager.getService().launchBugReportHandlerApp()) {
+ Log.w(TAG, "Bugreport handler could not be launched");
+ ActivityManager.getService().requestInteractiveBugReport();
+ }
} catch (RemoteException e) {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index 99c55f1..f815b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -79,12 +79,6 @@
});
}
- private void animate() {
- mAnimator.cancel();
- mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
- mAnimator.start();
- }
-
public float getReveal() {
return mReveal;
}
@@ -93,8 +87,8 @@
if (DEBUG) {
Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
}
+ mAnimator.cancel();
mAwake = awake;
- mAnimator.setDuration(duration);
if (duration == 0) {
// We are transiting from home to aod or aod to home directly,
// we don't need to do transition in these cases.
@@ -103,7 +97,9 @@
mRevealListener.onRevealStateChanged();
mRevealListener.onRevealEnd();
} else {
- animate();
+ mAnimator.setDuration(duration);
+ mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
+ mAnimator.start();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 0a9100f..f30c181 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -25,7 +25,6 @@
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Handler;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
@@ -34,8 +33,8 @@
import android.widget.Button;
import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.BgHandler;
-import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.QSTileHost;
@@ -47,6 +46,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -55,8 +55,8 @@
private final ArrayList<TileInfo> mTiles = new ArrayList<>();
private final ArraySet<String> mSpecs = new ArraySet<>();
- private final Handler mBgHandler;
- private final Handler mMainHandler;
+ private final Executor mMainExecutor;
+ private final Executor mBgExecutor;
private final Context mContext;
private TileStateListener mListener;
@@ -64,10 +64,10 @@
@Inject
public TileQueryHelper(Context context,
- @MainHandler Handler mainHandler, @BgHandler Handler bgHandler) {
+ @Main Executor mainExecutor, @Background Executor bgExecutor) {
mContext = context;
- mMainHandler = mainHandler;
- mBgHandler = bgHandler;
+ mMainExecutor = mainExecutor;
+ mBgExecutor = bgExecutor;
}
public void setListener(TileStateListener listener) {
@@ -126,7 +126,7 @@
tilesToAdd.add(tile);
}
- mBgHandler.post(() -> {
+ mBgExecutor.execute(() -> {
for (QSTile tile : tilesToAdd) {
final QSTile.State state = tile.getState().copy();
// Ignore the current state and get the generic label instead.
@@ -139,7 +139,7 @@
}
private void addPackageTiles(final QSTileHost host) {
- mBgHandler.post(() -> {
+ mBgExecutor.execute(() -> {
Collection<QSTile> params = host.getTiles();
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> services = pm.queryIntentServicesAsUser(
@@ -185,7 +185,7 @@
private void notifyTilesChanged(final boolean finished) {
final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles);
- mMainHandler.post(() -> {
+ mMainExecutor.execute(() -> {
if (mListener != null) {
mListener.onTilesChanged(tilesToReturn);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index fcdd234..f3e2f10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -162,6 +162,8 @@
if (runningTask.supportsSplitScreenMultiWindow) {
if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
runningTask.id, stackCreateMode, initialBounds)) {
+ mDividerOptional.ifPresent(Divider::onDockedTopTask);
+
// The overview service is handling split screen, so just skip the wait for the
// first draw and notify the divider to start animating now
mDividerOptional.ifPresent(Divider::onRecentsDrawn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 341c49a..2005d79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -17,14 +17,13 @@
package com.android.systemui.statusbar;
import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
-import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.Background;
import java.util.Map;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,10 +48,10 @@
private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>();
@Inject
- public FeatureFlags(@BgHandler Handler bgHandler) {
+ public FeatureFlags(@Background Executor executor) {
DeviceConfig.addOnPropertiesChangedListener(
"systemui",
- new HandlerExecutor(bgHandler),
+ executor,
this::onPropertiesChanged);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 9f5cf68..61043fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -36,6 +36,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.assist.AssistHandleViewController;
import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -168,6 +169,9 @@
View navigationWindow = navBar.getView().getRootView();
WindowManagerGlobal.getInstance()
.removeView(navigationWindow, true /* immediate */);
+ // Also remove FragmentHostState here in case that onViewDetachedFromWindow has not yet
+ // invoked after display removal.
+ FragmentHostManager.removeAndDestroy(navigationWindow);
mNavigationBars.remove(displayId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 4cc5b21..ff4ce94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -65,8 +65,18 @@
boolean needsRedaction(NotificationEntry entry);
+ /**
+ * Has the given user chosen to allow their private (full) notifications to be shown even
+ * when the lockscreen is in "public" (secure & locked) mode?
+ */
boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
+ /**
+ * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
+ * "public" (secure & locked) mode?
+ */
+ boolean userAllowsNotificationsInPublic(int userId);
+
/** Notified when the current user changes. */
interface UserChangedListener {
void onUserChanged(int userId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f5710a8..0f3f6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -435,7 +435,7 @@
* Has the given user chosen to allow notifications to be shown even when the lockscreen is in
* "public" (secure & locked) mode?
*/
- private boolean userAllowsNotificationsInPublic(int userHandle) {
+ public boolean userAllowsNotificationsInPublic(int userHandle) {
if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a98f826..d3a9c2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -62,6 +62,7 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -122,6 +123,7 @@
private final Context mContext;
private final MediaSessionManager mMediaSessionManager;
private final ArrayList<MediaListener> mMediaListeners;
+ private final Lazy<StatusBar> mStatusBarLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -182,6 +184,7 @@
public NotificationMediaManager(
Context context,
Lazy<ShadeController> shadeController,
+ Lazy<StatusBar> statusBarLazy,
Lazy<StatusBarWindowController> statusBarWindowController,
NotificationEntryManager notificationEntryManager,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -195,6 +198,8 @@
// TODO: use MediaSessionManager.SessionListener to hook us up to future updates
// in session state
mShadeController = shadeController;
+ // TODO: use KeyguardStateController#isOccluded to remove this dependency
+ mStatusBarLazy = statusBarLazy;
mStatusBarWindowController = statusBarWindowController;
mEntryManager = notificationEntryManager;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@@ -526,9 +531,8 @@
}
}
- ShadeController shadeController = mShadeController.get();
StatusBarWindowController windowController = mStatusBarWindowController.get();
- boolean hideBecauseOccluded = shadeController != null && shadeController.isOccluded();
+ boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
@@ -599,6 +603,7 @@
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
+ ShadeController shadeController = mShadeController.get();
boolean cannotAnimateDoze = shadeController != null
&& shadeController.isDozing()
&& !ScrimState.AOD.getAnimateChange();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index e5f44bd..b61c1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -36,7 +36,10 @@
import javax.inject.Inject;
import javax.inject.Singleton;
-/** Component which manages the various reasons a notification might be filtered out. */
+/** Component which manages the various reasons a notification might be filtered out.*/
+// TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174.
+// Notification filtering is taken care of across the different Coordinators (mostly
+// KeyguardCoordinator.java)
@Singleton
public class NotificationFilter {
@@ -109,7 +112,7 @@
return true;
}
- if (entry.isSuspended()) {
+ if (entry.getRanking().isSuspended()) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 3f7fd1a..7b1dc07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -64,6 +64,8 @@
}
};
+ // TODO: (b/145659174) remove after moving to NewNotifPipeline. Replaced by
+ // DeviceProvisionedCoordinator
private final DeviceProvisionedListener mDeviceProvisionedListener =
new DeviceProvisionedListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index f9f3266..9ae3882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -18,6 +18,8 @@
import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -34,7 +36,8 @@
private final List<NotificationEntry> mUnmodifiableChildren =
Collections.unmodifiableList(mChildren);
- GroupEntry(String key) {
+ @VisibleForTesting
+ public GroupEntry(String key) {
super(key);
}
@@ -52,7 +55,8 @@
return mUnmodifiableChildren;
}
- void setSummary(@Nullable NotificationEntry summary) {
+ @VisibleForTesting
+ public void setSummary(@Nullable NotificationEntry summary) {
mSummary = summary;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index dc68c4b..6ce7fd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -18,6 +18,8 @@
import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Abstract superclass for top-level entries, i.e. things that can appear in the final notification
* list shown to users. In practice, this means either GroupEntries or NotificationEntries.
@@ -49,7 +51,8 @@
return mParent;
}
- void setParent(@Nullable GroupEntry parent) {
+ @VisibleForTesting
+ public void setParent(@Nullable GroupEntry parent) {
mParent = parent;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 3eb55ef..232fb6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -275,10 +275,6 @@
return mRanking.getSuppressedVisualEffects();
}
- public boolean isSuspended() {
- return mRanking.isSuspended();
- }
-
/** @see Ranking#canBubble() */
public boolean canBubble() {
return mRanking.canBubble();
@@ -951,6 +947,15 @@
}
/**
+ * Whether or not this row represents a system notification. Note that if this is
+ * {@code null}, that means we were either unable to retrieve the info or have yet to
+ * retrieve the info.
+ */
+ public Boolean isSystemNotification() {
+ return mIsSystemNotification;
+ }
+
+ /**
* Set this notification to be sensitive.
*
* @param sensitive true if the content of this notification is sensitive right now
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 8bce528..48a4882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,36 +16,28 @@
package com.android.systemui.statusbar.notification.collection
-import android.app.Notification
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.NotificationManager.IMPORTANCE_MIN
-import android.app.Person
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import com.android.internal.annotations.VisibleForTesting
-
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.logging.NotifEvent
import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationGroupManager
import com.android.systemui.statusbar.policy.HeadsUpManager
-
-import java.util.Objects
-import java.util.ArrayList
-
-import javax.inject.Inject
-
-import kotlin.Comparator
-
import dagger.Lazy
+import java.util.Objects
+import javax.inject.Inject
private const val TAG = "NotifRankingManager"
@@ -64,7 +56,8 @@
private val headsUpManager: HeadsUpManager,
private val notifFilter: NotificationFilter,
private val notifLog: NotifLog,
- sectionsFeatureManager: NotificationSectionsFeatureManager
+ sectionsFeatureManager: NotificationSectionsFeatureManager,
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier
) {
var rankingMap: RankingMap? = null
@@ -79,6 +72,9 @@
val aRank = a.ranking.rank
val bRank = b.ranking.rank
+ val aIsPeople = a.isPeopleNotification()
+ val bIsPeople = b.isPeopleNotification()
+
val aMedia = isImportantMedia(a)
val bMedia = isImportantMedia(b)
@@ -88,25 +84,19 @@
val aHeadsUp = a.isRowHeadsUp
val bHeadsUp = b.isRowHeadsUp
- if (usePeopleFiltering && a.isPeopleNotification() != b.isPeopleNotification()) {
- if (a.isPeopleNotification()) -1 else 1
- } else if (aHeadsUp != bHeadsUp) {
- if (aHeadsUp) -1 else 1
- } else if (aHeadsUp) {
+ when {
+ usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+ aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
// Provide consistent ranking with headsUpManager
- headsUpManager.compare(a, b)
- } else if (aMedia != bMedia) {
+ aHeadsUp -> headsUpManager.compare(a, b)
// Upsort current media notification.
- if (aMedia) -1 else 1
- } else if (aSystemMax != bSystemMax) {
+ aMedia != bMedia -> if (aMedia) -1 else 1
// Upsort PRIORITY_MAX system notifications
- if (aSystemMax) -1 else 1
- } else if (a.isHighPriority != b.isHighPriority) {
- -1 * java.lang.Boolean.compare(a.isHighPriority, b.isHighPriority)
- } else if (aRank != bRank) {
- aRank - bRank
- } else {
- nb.notification.`when`.compareTo(na.notification.`when`)
+ aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1
+ a.isHighPriority != b.isHighPriority ->
+ -1 * a.isHighPriority.compareTo(b.isHighPriority)
+ aRank != bRank -> aRank - bRank
+ else -> nb.notification.`when`.compareTo(na.notification.`when`)
}
}
@@ -138,10 +128,9 @@
val c = entry.channel
val n = entry.sbn.notification
- if (((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
- n.hasMediaSession() ||
- n.hasPerson() ||
- n.hasStyle(Notification.MessagingStyle::class.java))) {
+ if ((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
+ n.hasMediaSession() ||
+ entry.isPeopleNotification()) {
// Users who have long pressed and demoted to silent should not see the notification
// in the top section
if (c != null && c.hasUserSetImportance()) {
@@ -204,7 +193,7 @@
isMedia: Boolean,
isSystemMax: Boolean
) {
- if (usePeopleFiltering && entry.hasAssociatedPeople()) {
+ if (usePeopleFiltering && entry.isPeopleNotification()) {
entry.bucket = BUCKET_PEOPLE
} else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
entry.bucket = BUCKET_ALERTING
@@ -235,6 +224,11 @@
}
}
}
+
+ private fun NotificationEntry.isPeopleNotification() =
+ sbn.isPeopleNotification()
+ private fun StatusBarNotification.isPeopleNotification() =
+ peopleNotificationIdentifier.isPeopleNotification(this)
}
// Convenience functions
@@ -245,16 +239,3 @@
private fun StatusBarNotification.isSystemNotification(): Boolean {
return "android" == packageName || "com.android.systemui" == packageName
}
-
-private fun Notification.hasPerson(): Boolean {
- val people: ArrayList<Person> =
- (extras?.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)) ?: ArrayList()
- return people.isNotEmpty()
-}
-
-private fun Notification.hasStyle(targetStyleClass: Class<*>): Boolean {
- return targetStyleClass == notificationStyle
-}
-
-private fun NotificationEntry.isPeopleNotification(): Boolean =
- sbn.notification.hasStyle(Notification.MessagingStyle::class.java)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
new file mode 100644
index 0000000..898918e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+
+/**
+ * Interface for registering callbacks to the {@link NewNotifPipeline}.
+ *
+ * This includes registering:
+ * {@link Pluggable}s to the {@link NotifListBuilder}
+ * {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s to {@link NotifCollection}
+ */
+public interface Coordinator {
+
+ /**
+ * Called after the NewNotifPipeline is initialized.
+ * Coordinators should register their {@link Pluggable}s to the notifListBuilder
+ * and their {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s
+ * to the notifCollection in this method.
+ */
+ void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
new file mode 100644
index 0000000..511aafc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters out most notifications when the device is unprovisioned.
+ * Special notifications with extra permissions and tags won't be filtered out even when the
+ * device is unprovisioned.
+ */
+@Singleton
+public class DeviceProvisionedCoordinator implements Coordinator {
+ private static final String TAG = "DeviceProvisionedCoordinator";
+
+ private final DeviceProvisionedController mDeviceProvisionedController;
+
+ @Inject
+ public DeviceProvisionedCoordinator(DeviceProvisionedController deviceProvisionedController) {
+ mDeviceProvisionedController = deviceProvisionedController;
+ }
+
+ @Override
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
+ notifListBuilder.addFilter(mNotifFilter);
+ }
+
+ protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ return !mDeviceProvisionedController.isDeviceProvisioned()
+ && !showNotificationEvenIfUnprovisioned(entry.getSbn());
+ }
+ };
+
+ /**
+ * Only notifications coming from packages with permission
+ * android.permission.NOTIFICATION_DURING_SETUP that also have special tags
+ * marking them as relevant for setup are allowed to show when device is unprovisioned
+ */
+ private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+ final boolean hasPermission = checkUidPermission(AppGlobals.getPackageManager(),
+ Manifest.permission.NOTIFICATION_DURING_SETUP,
+ sbn.getUid()) == PackageManager.PERMISSION_GRANTED;
+ return hasPermission
+ && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+ }
+
+ private static int checkUidPermission(IPackageManager packageManager, String permission,
+ int uid) {
+ try {
+ return packageManager.checkUidPermission(permission, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedListener =
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onDeviceProvisionedChanged() {
+ mNotifFilter.invalidateList();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
new file mode 100644
index 0000000..4803cf4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles ForegroundService interactions with notifications.
+ * Tags notifications with appOps.
+ * Lifetime extends notifications associated with an ongoing ForegroundService.
+ * Filters out notifications that represent foreground services that are no longer running
+ *
+ * Previously this logic lived in
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener
+ * frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
+ */
+@Singleton
+public class ForegroundCoordinator implements Coordinator {
+ private static final String TAG = "ForegroundNotificationCoordinator";
+
+ private final ForegroundServiceController mForegroundServiceController;
+ private final AppOpsController mAppOpsController;
+ private final Handler mMainHandler;
+ private final Handler mBgHandler;
+
+ private NotifCollection mNotifCollection;
+
+ @Inject
+ public ForegroundCoordinator(
+ ForegroundServiceController foregroundServiceController,
+ AppOpsController appOpsController,
+ @MainHandler Handler mainHandler,
+ @BgHandler Handler bgHandler) {
+ mForegroundServiceController = foregroundServiceController;
+ mAppOpsController = appOpsController;
+ mMainHandler = mainHandler;
+ mBgHandler = bgHandler;
+ }
+
+ @Override
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ mNotifCollection = notifCollection;
+
+ // extend the lifetime of foreground notification services to show for at least 5 seconds
+ mNotifCollection.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
+
+ // listen for new notifications to add appOps
+ mNotifCollection.addCollectionListener(mNotifCollectionListener);
+
+ // when appOps change, update any relevant notifications to update appOps for
+ mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
+
+ // filter out foreground service notifications that aren't necessary anymore
+ notifListBuilder.addFilter(mNotifFilter);
+ }
+
+ /**
+ * Filters out notifications that represent foreground services that are no longer running.
+ */
+ protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ StatusBarNotification sbn = entry.getSbn();
+ if (mForegroundServiceController.isDisclosureNotification(sbn)
+ && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
+ return true;
+ }
+
+ if (mForegroundServiceController.isSystemAlertNotification(sbn)) {
+ final String[] apps = sbn.getNotification().extras.getStringArray(
+ Notification.EXTRA_FOREGROUND_APPS);
+ if (apps != null && apps.length >= 1) {
+ if (!mForegroundServiceController.isSystemAlertWarningNeeded(
+ sbn.getUserId(), apps[0])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ };
+
+ /**
+ * Extends the lifetime of foreground notification services such that they show for at least
+ * five seconds
+ */
+ private final NotifLifetimeExtender mForegroundLifetimeExtender = new NotifLifetimeExtender() {
+ private static final int MIN_FGS_TIME_MS = 5000;
+ private OnEndLifetimeExtensionCallback mEndCallback;
+ private Map<String, Runnable> mEndRunnables = new HashMap<>();
+
+ @Override
+ public String getName() {
+ return TAG;
+ }
+
+ @Override
+ public void setCallback(OnEndLifetimeExtensionCallback callback) {
+ mEndCallback = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+ if ((entry.getSbn().getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
+ return false;
+ }
+
+ final long currTime = System.currentTimeMillis();
+ final boolean extendLife = currTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
+
+ if (extendLife) {
+ if (!mEndRunnables.containsKey(entry.getKey())) {
+ final Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ mEndCallback.onEndLifetimeExtension(mForegroundLifetimeExtender, entry);
+ }
+ };
+ mEndRunnables.put(entry.getKey(), runnable);
+ mBgHandler.postDelayed(runnable, MIN_FGS_TIME_MS
+ - (currTime - entry.getSbn().getPostTime()));
+ }
+ }
+
+ return extendLife;
+ }
+
+ @Override
+ public void cancelLifetimeExtension(NotificationEntry entry) {
+ if (mEndRunnables.containsKey(entry.getKey())) {
+ Runnable endRunnable = mEndRunnables.remove(entry.getKey());
+ mBgHandler.removeCallbacks(endRunnable);
+ }
+ }
+ };
+
+ /**
+ * Adds appOps to incoming and updating notifications
+ */
+ private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+ @Override
+ public void onEntryAdded(NotificationEntry entry) {
+ tagForeground(entry);
+ }
+
+ @Override
+ public void onEntryUpdated(NotificationEntry entry) {
+ tagForeground(entry);
+ }
+
+ private void tagForeground(NotificationEntry entry) {
+ final StatusBarNotification sbn = entry.getSbn();
+ // note: requires that the ForegroundServiceController is updating their appOps first
+ ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(sbn.getUserId(),
+ sbn.getPackageName());
+ if (activeOps != null) {
+ synchronized (entry.mActiveAppOps) {
+ entry.mActiveAppOps.clear();
+ entry.mActiveAppOps.addAll(activeOps);
+ }
+ }
+ }
+ };
+
+ /**
+ * Update the appOp for the posted notification associated with the current foreground service
+ * @param code code for appOp to add/remove
+ * @param uid of user the notification is sent to
+ * @param packageName package that created the notification
+ * @param active whether the appOpCode is active or not
+ */
+ private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+ int userId = UserHandle.getUserId(uid);
+
+ // Update appOp if there's an associated posted notification:
+ final String foregroundKey = mForegroundServiceController.getStandardLayoutKey(userId,
+ packageName);
+ if (foregroundKey != null) {
+ final NotificationEntry entry = findNotificationEntryWithKey(foregroundKey);
+ if (entry != null
+ && uid == entry.getSbn().getUid()
+ && packageName.equals(entry.getSbn().getPackageName())) {
+ boolean changed;
+ synchronized (entry.mActiveAppOps) {
+ if (active) {
+ changed = entry.mActiveAppOps.add(code);
+ } else {
+ changed = entry.mActiveAppOps.remove(code);
+ }
+ }
+ if (changed) {
+ mMainHandler.post(mNotifFilter::invalidateList);
+ }
+ }
+ }
+ }
+
+ private NotificationEntry findNotificationEntryWithKey(String key) {
+ for (NotificationEntry entry : mNotifCollection.getNotifs()) {
+ if (entry.getKey().equals(key)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
new file mode 100644
index 0000000..6daf3fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.app.Notification.VISIBILITY_SECRET;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.MainThread;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters low priority and privacy-sensitive notifications from the lockscreen.
+ */
+@Singleton
+public class KeyguardCoordinator implements Coordinator {
+ private static final String TAG = "KeyguardNotificationCoordinator";
+
+ private final Context mContext;
+ private final Handler mMainHandler;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final StatusBarStateController mStatusBarStateController;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ @Inject
+ public KeyguardCoordinator(
+ Context context,
+ @MainThread Handler mainThreadHandler,
+ KeyguardStateController keyguardStateController,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ BroadcastDispatcher broadcastDispatcher,
+ StatusBarStateController statusBarStateController,
+ KeyguardUpdateMonitor keyguardUpdateMonitor) {
+ mContext = context;
+ mMainHandler = mainThreadHandler;
+ mKeyguardStateController = keyguardStateController;
+ mLockscreenUserManager = lockscreenUserManager;
+
+ mBroadcastDispatcher = broadcastDispatcher;
+ mStatusBarStateController = statusBarStateController;
+ mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ }
+
+ @Override
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ setupInvalidateNotifListCallbacks();
+ notifListBuilder.addFilter(mNotifFilter);
+ }
+
+ protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ final StatusBarNotification sbn = entry.getSbn();
+
+ // FILTER OUT the notification when the notification isn't for the current profile
+ if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) {
+ return true;
+ }
+
+ // FILTER OUT the notification when the keyguard is showing and...
+ if (mKeyguardStateController.isShowing()) {
+ // ... user settings or the device policy manager doesn't allow lockscreen
+ // notifications;
+ if (!mLockscreenUserManager.shouldShowLockscreenNotifications()) {
+ return true;
+ }
+
+ final int currUserId = mLockscreenUserManager.getCurrentUserId();
+ final int notifUserId = (sbn.getUser().getIdentifier() == UserHandle.USER_ALL)
+ ? currUserId : sbn.getUser().getIdentifier();
+
+ // ... user is in lockdown
+ if (mKeyguardUpdateMonitor.isUserInLockdown(currUserId)
+ || mKeyguardUpdateMonitor.isUserInLockdown(notifUserId)) {
+ return true;
+ }
+
+ // ... device is in public mode and the user's settings doesn't allow
+ // notifications to show in public mode
+ if (mLockscreenUserManager.isLockscreenPublicMode(currUserId)
+ || mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) {
+ if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) {
+ return true;
+ }
+
+ if (!mLockscreenUserManager.userAllowsNotificationsInPublic(currUserId)
+ || !mLockscreenUserManager.userAllowsNotificationsInPublic(
+ notifUserId)) {
+ return true;
+ }
+ }
+
+ // ... neither this notification nor its summary have high enough priority
+ // to be shown on the lockscreen
+ // TODO: grouping hasn't happened yet (b/145134683)
+ if (entry.getParent() != null) {
+ final NotificationEntry summary = entry.getParent().getRepresentativeEntry();
+ if (priorityExceedsLockscreenShowingThreshold(summary)) {
+ return false;
+ }
+ }
+ return !priorityExceedsLockscreenShowingThreshold(entry);
+ }
+ return false;
+ }
+ };
+
+ private boolean priorityExceedsLockscreenShowingThreshold(NotificationEntry entry) {
+ if (entry == null) {
+ return false;
+ }
+ if (NotificationUtils.useNewInterruptionModel(mContext)
+ && hideSilentNotificationsOnLockscreen()) {
+ // TODO: make sure in the NewNotifPipeline that entry.isHighPriority() has been
+ // correctly updated before reaching this point (b/145134683)
+ return entry.isHighPriority();
+ } else {
+ return !entry.getRanking().isAmbient();
+ }
+ }
+
+ private boolean hideSilentNotificationsOnLockscreen() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0;
+ }
+
+ private void setupInvalidateNotifListCallbacks() {
+ // register onKeyguardShowing callback
+ mKeyguardStateController.addCallback(mKeyguardCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+
+ // register lockscreen settings changed callbacks:
+ final ContentObserver settingsObserver = new ContentObserver(mMainHandler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (mKeyguardStateController.isShowing()) {
+ invalidateListFromFilter("Settings " + uri + " changed");
+ }
+ }
+ };
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS),
+ false,
+ settingsObserver,
+ UserHandle.USER_ALL);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
+ false,
+ settingsObserver);
+
+ // register (maybe) public mode changed callbacks:
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mKeyguardStateController.isShowing()) {
+ // maybe public mode changed
+ invalidateListFromFilter(intent.getAction());
+ }
+ }}, new IntentFilter(Intent.ACTION_USER_SWITCHED));
+ }
+
+ private void invalidateListFromFilter(String reason) {
+ mNotifFilter.invalidateList();
+ }
+
+ private final KeyguardStateController.Callback mKeyguardCallback =
+ new KeyguardStateController.Callback() {
+ @Override
+ public void onUnlockedChanged() {
+ invalidateListFromFilter("onUnlockedChanged");
+ }
+
+ @Override
+ public void onKeyguardShowingChanged() {
+ invalidateListFromFilter("onKeyguardShowingChanged");
+ }
+ };
+
+ private final StatusBarStateController.StateListener mStatusBarStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ // maybe public mode changed
+ invalidateListFromFilter("onStatusBarStateChanged");
+ }
+ };
+
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ // maybe lockdown mode changed
+ invalidateListFromFilter("onStrongAuthStateChanged");
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
new file mode 100644
index 0000000..13247193
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles the attachment of the {@link NotifListBuilder} and {@link NotifCollection} to the
+ * {@link Coordinator}s, so that the Coordinators can register their respective callbacks.
+ */
+@Singleton
+public class NotifCoordinators implements Dumpable {
+ private static final String TAG = "NotifCoordinators";
+ private final List<Coordinator> mCoordinators = new ArrayList<>();
+
+ /**
+ * Creates all the coordinators.
+ */
+ @Inject
+ public NotifCoordinators(
+ KeyguardCoordinator keyguardCoordinator,
+ RankingCoordinator rankingCoordinator,
+ ForegroundCoordinator foregroundCoordinator,
+ DeviceProvisionedCoordinator deviceProvisionedCoordinator) {
+ mCoordinators.add(keyguardCoordinator);
+ mCoordinators.add(rankingCoordinator);
+ mCoordinators.add(foregroundCoordinator);
+ mCoordinators.add(deviceProvisionedCoordinator);
+ // TODO: add new Coordinators here! (b/145134683, b/112656837)
+ }
+
+ /**
+ * Sends the initialized notifListBuilder and notifCollection to each
+ * coordinator to indicate the notifListBuilder is ready to accept {@link Pluggable}s
+ * and the notifCollection is ready to accept {@link NotifCollectionListener}s and
+ * {@link NotifLifetimeExtender}s.
+ */
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ for (Coordinator c : mCoordinators) {
+ c.attach(notifCollection, notifListBuilder);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println(TAG + ":");
+ for (Coordinator c : mCoordinators) {
+ pw.println("\t" + c.getClass());
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
new file mode 100644
index 0000000..c390f96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters out NotificationEntries based on its Ranking.
+ */
+@Singleton
+public class RankingCoordinator implements Coordinator {
+ private static final String TAG = "RankingNotificationCoordinator";
+
+ private final StatusBarStateController mStatusBarStateController;
+
+ @Inject
+ public RankingCoordinator(StatusBarStateController statusBarStateController) {
+ mStatusBarStateController = statusBarStateController;
+ }
+
+ @Override
+ public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+ mStatusBarStateController.addCallback(mStatusBarStateCallback);
+
+ notifListBuilder.addFilter(mNotifFilter);
+ }
+
+ /**
+ * Checks whether to filter out the given notification based the notification's Ranking object.
+ * NotifListBuilder invalidates the notification list each time the ranking is updated,
+ * so we don't need to explicitly invalidate this filter on ranking update.
+ */
+ protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+ @Override
+ public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ // App suspended from Ranking
+ if (entry.getRanking().isSuspended()) {
+ return true;
+ }
+
+ // Dozing + DND Settings from Ranking object
+ if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
+ return true;
+ }
+
+ if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+
+ private final StatusBarStateController.StateListener mStatusBarStateCallback =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ mNotifFilter.invalidateList();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 3b3e7e2..5fc55da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -37,6 +38,7 @@
public class NewNotifPipeline implements Dumpable {
private final NotifCollection mNotifCollection;
private final NotifListBuilderImpl mNotifPipeline;
+ private final NotifCoordinators mNotifPluggableCoordinators;
private final DumpController mDumpController;
private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
@@ -45,9 +47,11 @@
public NewNotifPipeline(
NotifCollection notifCollection,
NotifListBuilderImpl notifPipeline,
+ NotifCoordinators notifCoordinators,
DumpController dumpController) {
mNotifCollection = notifCollection;
mNotifPipeline = notifPipeline;
+ mNotifPluggableCoordinators = notifCoordinators;
mDumpController = dumpController;
}
@@ -57,6 +61,7 @@
mFakePipelineConsumer.attach(mNotifPipeline);
mNotifPipeline.attach(mNotifCollection);
mNotifCollection.attach(notificationService);
+ mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
Log.d(TAG, "Notif pipeline initialized");
@@ -66,6 +71,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mFakePipelineConsumer.dump(fd, pw, args);
+ mNotifPluggableCoordinators.dump(fd, pw, args);
}
private static final String TAG = "NewNotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index fdd51e9e..8e9a051e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -318,7 +318,7 @@
}
mParent.addView(mPeopleHubView, targetIndex);
return true;
- } else if (currentHubIndex != targetIndex - 1) {
+ } else if (currentHubIndex != targetIndex) {
if (currentHubIndex < targetIndex) {
targetIndex--;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 063ad85..09ebb64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -88,10 +88,9 @@
private final PulseExpansionHandler mPulseExpansionHandler;
private final StatusBarWindowController mStatusBarWindowController;
private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
- private final StatusBarWindowViewController mStatusBarWindowViewController;
+ private StatusBarWindowViewController mStatusBarWindowViewController;
private final LockscreenLockIconController mLockscreenLockIconController;
private NotificationIconAreaController mNotificationIconAreaController;
- private StatusBarWindowView mStatusBarWindow;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private NotificationPanelView mNotificationPanel;
private View mAmbientIndicationContainer;
@@ -112,7 +111,6 @@
PulseExpansionHandler pulseExpansionHandler,
StatusBarWindowController statusBarWindowController,
NotificationWakeUpCoordinator notificationWakeUpCoordinator,
- StatusBarWindowViewController statusBarWindowViewController,
LockscreenLockIconController lockscreenLockIconController) {
super();
mDozeLog = dozeLog;
@@ -132,7 +130,6 @@
mPulseExpansionHandler = pulseExpansionHandler;
mStatusBarWindowController = statusBarWindowController;
mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
- mStatusBarWindowViewController = statusBarWindowViewController;
mLockscreenLockIconController = lockscreenLockIconController;
}
@@ -143,14 +140,14 @@
*/
public void initialize(StatusBar statusBar,
NotificationIconAreaController notificationIconAreaController,
- StatusBarWindowView statusBarWindow,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+ StatusBarWindowViewController statusBarWindowViewController,
NotificationPanelView notificationPanel, View ambientIndicationContainer) {
mStatusBar = statusBar;
mNotificationIconAreaController = notificationIconAreaController;
- mStatusBarWindow = statusBarWindow;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationPanel = notificationPanel;
+ mStatusBarWindowViewController = statusBarWindowViewController;
mAmbientIndicationContainer = ambientIndicationContainer;
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 4d6b54c..f3e9b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -134,6 +134,7 @@
if (fd != null) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inPreferredConfig = Bitmap.Config.HARDWARE;
return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
fd.getFileDescriptor(), null, options));
} catch (OutOfMemoryError e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index f359fe7..b31ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -77,14 +77,6 @@
void goToKeyguard();
/**
- * When the keyguard is showing and covered by something (bouncer, keyguard activity, etc.) it
- * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
- *
- * @return whether the keyguard is currently occluded
- */
- boolean isOccluded();
-
- /**
* Notify the shade controller that the current user changed
*
* @param newUserId userId of the new user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 170261e..5c71a57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -237,9 +237,9 @@
import java.util.Optional;
import javax.inject.Named;
+import javax.inject.Provider;
import dagger.Lazy;
-import dagger.Subcomponent;
public class StatusBar extends SystemUI implements DemoMode,
ActivityStarter, KeyguardStateController.Callback,
@@ -377,9 +377,10 @@
private final FalsingManager mFalsingManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ConfigurationController mConfigurationController;
- private final StatusBarWindowViewController mStatusBarWindowViewController;
+ protected StatusBarWindowViewController mStatusBarWindowViewController;
private final DozeParameters mDozeParameters;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
private final PluginManager mPluginManager;
private final RemoteInputUriController mRemoteInputUriController;
private final Optional<Divider> mDividerOptional;
@@ -583,6 +584,8 @@
}
}
+ // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+ // KeyguardCoordinator
@Override
public void onStrongAuthStateChanged(int userId) {
super.onStrongAuthStateChanged(userId);
@@ -659,7 +662,6 @@
NotificationListener notificationListener,
ConfigurationController configurationController,
StatusBarWindowController statusBarWindowController,
- StatusBarWindowViewController statusBarWindowViewController,
LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@@ -673,6 +675,7 @@
VolumeComponent volumeComponent,
CommandQueue commandQueue,
Optional<Recents> recentsOptional,
+ Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
@@ -732,7 +735,6 @@
mNotificationListener = notificationListener;
mConfigurationController = configurationController;
mStatusBarWindowController = statusBarWindowController;
- mStatusBarWindowViewController = statusBarWindowViewController;
mLockscreenLockIconController = lockscreenLockIconController;
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
@@ -746,6 +748,7 @@
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
mRecentsOptional = recentsOptional;
+ mStatusBarComponentBuilder = statusBarComponentBuilder;
mPluginManager = pluginManager;
mRemoteInputUriController = remoteInputUriController;
mDividerOptional = dividerOptional;
@@ -895,7 +898,8 @@
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mDozeServiceHost.initialize(this, mNotificationIconAreaController,
- mStatusBarWindow, mStatusBarKeyguardViewManager,
+ mStatusBarKeyguardViewManager,
+ mStatusBarWindowViewController,
mNotificationPanel, mAmbientIndicationContainer);
Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
@@ -962,7 +966,7 @@
updateResources();
updateTheme();
- inflateStatusBarWindow(context);
+ inflateStatusBarWindow();
mStatusBarWindowViewController.setService(this);
mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
@@ -1233,7 +1237,8 @@
mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
- mNotificationAlertingManager, rowBinder, mKeyguardStateController, mCommandQueue);
+ mNotificationAlertingManager, rowBinder, mKeyguardStateController,
+ this /* statusBar */, mCommandQueue);
mNotificationListController =
new NotificationListController(
@@ -1246,7 +1251,7 @@
mNotificationActivityStarter =
mStatusBarNotificationActivityStarterBuilder
- .setShadeController(this)
+ .setStatusBar(this)
.setActivityLaunchAnimator(mActivityLaunchAnimator)
.setNotificationPresenter(mPresenter)
.build();
@@ -1380,8 +1385,11 @@
mNotificationPanel);
}
- protected void inflateStatusBarWindow(Context context) {
+ private void inflateStatusBarWindow() {
mStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
+ StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
+ .statusBarWindowView(mStatusBarWindow).build();
+ mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController();
mStatusBarWindowViewController.setupExpandedStatusBar();
}
@@ -1751,7 +1759,12 @@
return mAmbientIndicationContainer;
}
- @Override
+ /**
+ * When the keyguard is showing and covered by a "showWhenLocked" activity it
+ * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
+ *
+ * @return whether the keyguard is currently occluded
+ */
public boolean isOccluded() {
return mIsOccluded;
}
@@ -4391,11 +4404,6 @@
return mGutsManager;
}
- @Subcomponent
- public interface StatusBarInjector {
- void createStatusBar(StatusBar statusbar);
- }
-
boolean isTransientShown() {
return mTransientShown;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
new file mode 100644
index 0000000..f3c843c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent tied to the lifecycle of StatusBar views.
+ */
+@Subcomponent
+public interface StatusBarComponent {
+ /**
+ * Builder for {@link StatusBarComponent}.
+ */
+ @Subcomponent.Builder
+ interface Builder {
+ @BindsInstance Builder statusBarWindowView(StatusBarWindowView statusBarWindowView);
+ StatusBarComponent build();
+ }
+
+ /**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface StatusBarScope {}
+
+ /**
+ * Creates a StatusBarWindowViewController.
+ */
+ @StatusBarScope
+ StatusBarWindowViewController getStatusBarWindowViewController();
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 5d69409..312c85f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -77,6 +77,7 @@
import java.util.Optional;
import javax.inject.Named;
+import javax.inject.Provider;
import javax.inject.Singleton;
import dagger.Lazy;
@@ -143,7 +144,6 @@
NotificationListener notificationListener,
ConfigurationController configurationController,
StatusBarWindowController statusBarWindowController,
- StatusBarWindowViewController statusBarWindowViewController,
LockscreenLockIconController lockscreenLockIconController,
DozeParameters dozeParameters,
ScrimController scrimController,
@@ -157,6 +157,7 @@
VolumeComponent volumeComponent,
CommandQueue commandQueue,
Optional<Recents> recentsOptional,
+ Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
RemoteInputUriController remoteInputUriController,
Optional<Divider> dividerOptional,
@@ -217,7 +218,6 @@
notificationListener,
configurationController,
statusBarWindowController,
- statusBarWindowViewController,
lockscreenLockIconController,
dozeParameters,
scrimController,
@@ -231,6 +231,7 @@
volumeComponent,
commandQueue,
recentsOptional,
+ statusBarComponentBuilder,
pluginManager,
remoteInputUriController,
dividerOptional,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 863874e..e283258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -93,6 +93,7 @@
private final NotificationRemoteInputManager mRemoteInputManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final ShadeController mShadeController;
+ private final StatusBar mStatusBar;
private final KeyguardStateController mKeyguardStateController;
private final ActivityStarter mActivityStarter;
private final NotificationEntryManager mEntryManager;
@@ -125,7 +126,8 @@
IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager,
StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager,
NotificationLockscreenUserManager lockscreenUserManager,
- ShadeController shadeController, KeyguardStateController keyguardStateController,
+ ShadeController shadeController, StatusBar statusBar,
+ KeyguardStateController keyguardStateController,
NotificationInterruptionStateProvider notificationInterruptionStateProvider,
MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
Handler mainThreadHandler, Handler backgroundHandler,
@@ -142,6 +144,8 @@
mRemoteInputManager = remoteInputManager;
mLockscreenUserManager = lockscreenUserManager;
mShadeController = shadeController;
+ // TODO: use KeyguardStateController#isOccluded to remove this dependency
+ mStatusBar = statusBar;
mKeyguardStateController = keyguardStateController;
mActivityStarter = activityStarter;
mEntryManager = entryManager;
@@ -198,7 +202,7 @@
final boolean afterKeyguardGone = isActivityIntent
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
- final boolean wasOccluded = mShadeController.isOccluded();
+ final boolean wasOccluded = mStatusBar.isOccluded();
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
@@ -253,7 +257,7 @@
mShadeController.addPostCollapseAction(runnable);
mShadeController.collapsePanel(true /* animate */);
} else if (mKeyguardStateController.isShowing()
- && mShadeController.isOccluded()) {
+ && mStatusBar.isOccluded()) {
mShadeController.addAfterKeyguardGoneRunnable(runnable);
mShadeController.collapsePanel();
} else {
@@ -384,7 +388,7 @@
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
mActivityLaunchAnimator.getLaunchAnimation(
- row, mShadeController.isOccluded())),
+ row, mStatusBar.isOccluded())),
new UserHandle(UserHandle.getUserId(appUid)));
mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
if (shouldCollapse()) {
@@ -518,6 +522,7 @@
private ShadeController mShadeController;
private NotificationPresenter mNotificationPresenter;
private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private StatusBar mStatusBar;
@Inject
public Builder(Context context,
@@ -567,8 +572,11 @@
mBubbleController = bubbleController;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
}
- public Builder setShadeController(ShadeController shadeController) {
- mShadeController = shadeController;
+
+ /** Sets the status bar to use as {@link StatusBar} and {@link ShadeController}. */
+ public Builder setStatusBar(StatusBar statusBar) {
+ mStatusBar = statusBar;
+ mShadeController = statusBar;
return this;
}
@@ -600,6 +608,7 @@
mGroupManager,
mLockscreenUserManager,
mShadeController,
+ mStatusBar,
mKeyguardStateController,
mNotificationInterruptionStateProvider,
mMetricsLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 30e26e5..6650cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -113,6 +113,7 @@
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final Context mContext;
+ private final StatusBar mStatusBar;
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
@@ -140,12 +141,15 @@
NotificationAlertingManager notificationAlertingManager,
NotificationRowBinderImpl notificationRowBinder,
KeyguardStateController keyguardStateController,
+ StatusBar statusBar,
CommandQueue commandQueue) {
mContext = context;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
+ // TODO: use KeyguardStateController#isOccluded to remove this dependency
+ mStatusBar = statusBar;
mCommandQueue = commandQueue;
mAboveShelfObserver = new AboveShelfObserver(stackScroller);
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -330,7 +334,7 @@
}
public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
- if (mShadeController.isOccluded()) {
+ if (mStatusBar.isOccluded()) {
boolean devicePublic = mLockscreenUserManager.
isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -356,7 +360,7 @@
} else {
// we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
return !mKeyguardStateController.isShowing()
- || mShadeController.isOccluded();
+ || mStatusBar.isOccluded();
}
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index feac5da..3935df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -43,7 +43,6 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -57,14 +56,13 @@
import java.io.PrintWriter;
import javax.inject.Inject;
-import javax.inject.Singleton;
import dagger.Lazy;
/**
* Controller for {@link StatusBarWindowView}.
*/
-@Singleton
+//@Singleton
public class StatusBarWindowViewController {
private final InjectionInflationController mInjectionInflationController;
private final NotificationWakeUpCoordinator mCoordinator;
@@ -116,9 +114,9 @@
DozeLog dozeLog,
DozeParameters dozeParameters,
CommandQueue commandQueue,
- SuperStatusBarViewFactory superStatusBarViewFactory,
Lazy<ShadeController> shadeControllerLazy,
- DockManager dockManager) {
+ DockManager dockManager,
+ StatusBarWindowView statusBarWindowView) {
mInjectionInflationController = injectionInflationController;
mCoordinator = coordinator;
mPulseExpansionHandler = pulseExpansionHandler;
@@ -134,7 +132,7 @@
mDozeLog = dozeLog;
mDozeParameters = dozeParameters;
mCommandQueue = commandQueue;
- mView = superStatusBarViewFactory.getStatusBarWindowView();
+ mView = statusBarWindowView;
mShadeControllerLazy = shadeControllerLazy;
mDockManager = dockManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 3f25bb6..b0cd90c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -50,9 +50,9 @@
import java.io.PrintWriter;
import java.util.BitSet;
-import java.util.concurrent.Executor;
-import java.util.Objects;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -552,8 +552,8 @@
// If this is the data subscription, update the currentState data name
if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null
&& mCurrentState.dataSim
- && !TextUtils.isEmpty(mServiceState.getDataOperatorAlphaShort())) {
- mCurrentState.networkNameData = mServiceState.getDataOperatorAlphaShort();
+ && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
+ mCurrentState.networkNameData = mServiceState.getOperatorAlphaShort();
}
notifyListenersIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
new file mode 100644
index 0000000..24f49ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+
+import java.util.concurrent.Executor;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for classes found within the concurrent package.
+ */
+@Module
+public abstract class ConcurrencyModule {
+ /**
+ * Provide a Background-Thread Executor by default.
+ */
+ @Provides
+ public static Executor provideExecutor(@BgHandler Handler handler) {
+ return new ExecutorImpl(handler);
+ }
+
+ /**
+ * Provide a Background-Thread Executor.
+ */
+ @Provides
+ @Background
+ public static Executor provideBackgroundExecutor(@BgHandler Handler handler) {
+ return new ExecutorImpl(handler);
+ }
+
+ /**
+ * Provide a Main-Thread Executor.
+ */
+ @Provides
+ @Main
+ public static Executor provideMainExecutor(Context context) {
+ return context.getMainExecutor();
+ }
+
+ /**
+ * Provide a Background-Thread Executor by default.
+ */
+ @Provides
+ public static DelayableExecutor provideDelayableExecutor(@BgHandler Handler handler) {
+ return new ExecutorImpl(handler);
+ }
+
+ /**
+ * Provide a Background-Thread Executor.
+ */
+ @Provides
+ @Background
+ public static DelayableExecutor provideBackgroundDelayableExecutor(@BgHandler Handler handler) {
+ return new ExecutorImpl(handler);
+ }
+
+ /**
+ * Provide a Main-Thread Executor.
+ */
+ @Provides
+ @Main
+ public static DelayableExecutor provideMainDelayableExecutor(@MainHandler Handler handler) {
+ return new ExecutorImpl(handler);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
new file mode 100644
index 0000000..2d6c4a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A sub-class of {@link Executor} that allows Runnables to be delayed and/or cancelled.
+ */
+public interface DelayableExecutor extends Executor {
+ /**
+ * Execute supplied Runnable on the Executors thread after a specified delay.
+ *
+ * See {@link android.os.Handler#postDelayed(Runnable, long)}.
+ *
+ * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+ */
+ default Runnable executeDelayed(Runnable r, long delayMillis) {
+ return executeDelayed(r, delayMillis, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Execute supplied Runnable on the Executors thread after a specified delay.
+ *
+ * See {@link android.os.Handler#postDelayed(Runnable, long)}.
+ *
+ * @return A Runnable that, when run, removes the supplied argument from the Executor queue..
+ */
+ Runnable executeDelayed(Runnable r, long delay, TimeUnit unit);
+
+ /**
+ * Execute supplied Runnable on the Executors thread at a specified uptime.
+ *
+ * See {@link android.os.Handler#postAtTime(Runnable, long)}.
+ *
+ * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+ */
+ default Runnable executeAtTime(Runnable r, long uptime) {
+ return executeAtTime(r, uptime, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Execute supplied Runnable on the Executors thread at a specified uptime.
+ *
+ * See {@link android.os.Handler#postAtTime(Runnable, long)}.
+ *
+ * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+ */
+ Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit);
+
+ /**
+ * Remove all pending Runnables.
+ *
+ */
+ void removeAll();
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
new file mode 100644
index 0000000..2a99de3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementations of {@link DelayableExecutor} for SystemUI.
+ */
+public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor {
+ private final Handler mHandler;
+
+ public ExecutorImpl(Handler handler) {
+ super(handler);
+ mHandler = handler;
+ }
+
+ @Override
+ public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
+ Object token = new Object();
+ Message m = mHandler.obtainMessage(0, token);
+ mHandler.sendMessageDelayed(m, unit.toMillis(delay));
+
+ return () -> mHandler.removeCallbacksAndMessages(token);
+ }
+
+ @Override
+ public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) {
+ Object token = new Object();
+ Message m = mHandler.obtainMessage(0, token);
+ mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis));
+
+ return () -> mHandler.removeCallbacksAndMessages(token);
+ }
+
+ @Override
+ public void removeAll() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 0990e22..4cb5472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import junit.framework.Assert;
@@ -70,6 +71,7 @@
@Mock private NotificationEntryManager mEntryManager;
@Mock private AppOpsController mAppOpsController;
@Mock private Handler mMainHandler;
+ @Mock private NotifCollection mNotifCollection;
@Before
public void setUp() throws Exception {
@@ -79,7 +81,7 @@
MockitoAnnotations.initMocks(this);
mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
mListener = new ForegroundServiceNotificationListener(
- mContext, mFsc, mEntryManager);
+ mContext, mFsc, mEntryManager, mNotifCollection);
ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
ArgumentCaptor.forClass(NotificationEntryListener.class);
verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index b41512c..39ce8c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -36,13 +36,9 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.Looper;
import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -52,6 +48,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -69,7 +67,6 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
public class TileQueryHelperTest extends SysuiTestCase {
private static final String CURRENT_TILES = "wifi,dnd,nfc";
private static final String ONLY_STOCK_TILES = "wifi,dnd";
@@ -98,14 +95,13 @@
private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
private QSTile.State mState;
- private TestableLooper mBGLooper;
private TileQueryHelper mTileQueryHelper;
- private Handler mMainHandler;
+ private FakeExecutor mMainExecutor;
+ private FakeExecutor mBgExecutor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mBGLooper = TestableLooper.get(this);
mContext.setMockPackageManager(mPackageManager);
mState = new QSTile.State();
@@ -123,9 +119,11 @@
}
).when(mQSTileHost).createTile(anyString());
- mMainHandler = new Handler(Looper.getMainLooper());
- mTileQueryHelper = new TileQueryHelper(mContext, mMainHandler,
- new Handler(mBGLooper.getLooper()));
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ mMainExecutor = new FakeExecutor(clock);
+ mBgExecutor = new FakeExecutor(clock);
+ mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor);
mTileQueryHelper.setListener(mListener);
}
@@ -138,8 +136,7 @@
public void testIsFinished_trueAfterQuerying() {
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
assertTrue(mTileQueryHelper.isFinished());
}
@@ -148,8 +145,7 @@
public void testQueryTiles_callsListenerTwice() {
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mListener, times(2)).onTilesChanged(any());
}
@@ -163,8 +159,7 @@
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
assertTrue(mTileQueryHelper.isFinished());
}
@@ -178,8 +173,7 @@
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
List<String> specs = new ArrayList<>();
@@ -199,8 +193,7 @@
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
List<String> specs = new ArrayList<>();
@@ -220,8 +213,7 @@
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
List<String> specs = new ArrayList<>();
@@ -251,8 +243,7 @@
"");
mTileQueryHelper.queryTiles(mQSTileHost);
- mBGLooper.processAllMessages();
- waitForIdleSync(mMainHandler);
+ FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
List<TileQueryHelper.TileInfo> tileInfos = mCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index a98945f..e0dfe7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -85,6 +85,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
@@ -228,7 +229,8 @@
mHeadsUpManager,
mock(NotificationFilter.class),
mNotifLog,
- mock(NotificationSectionsFeatureManager.class)),
+ mock(NotificationSectionsFeatureManager.class),
+ mock(PeopleNotificationIdentifier.class)),
mEnvironment
);
Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 01b2f89..cda1538e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -20,7 +20,6 @@
import android.app.NotificationChannel
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_LOW
-import android.app.Person
import android.service.notification.NotificationListenerService.RankingMap
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
@@ -36,6 +35,7 @@
import com.android.systemui.statusbar.notification.NotificationFilter
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -52,42 +52,39 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class NotificationRankingManagerTest
- : SysuiTestCase() {
+class NotificationRankingManagerTest : SysuiTestCase() {
- private var lazyMedia: Lazy<NotificationMediaManager> = Lazy {
- mock<NotificationMediaManager>(NotificationMediaManager::class.java)
+ private val lazyMedia: Lazy<NotificationMediaManager> = Lazy {
+ mock(NotificationMediaManager::class.java)
}
-
- private val rankingManager = TestableNotificationRankingManager(
- lazyMedia,
- mock<NotificationGroupManager>(NotificationGroupManager::class.java),
- mock<HeadsUpManager>(HeadsUpManager::class.java),
- mock<NotificationFilter>(NotificationFilter::class.java),
- mock<NotifLog>(NotifLog::class.java),
- mock<NotificationSectionsFeatureManager>(NotificationSectionsFeatureManager::class.java)
- )
+ private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier
+ private lateinit var rankingManager: TestableNotificationRankingManager
@Before
fun setup() {
+ personNotificationIdentifier =
+ mock(PeopleNotificationIdentifier::class.java)
+ rankingManager = TestableNotificationRankingManager(
+ lazyMedia,
+ mock(NotificationGroupManager::class.java),
+ mock(HeadsUpManager::class.java),
+ mock(NotificationFilter::class.java),
+ mock(NotifLog::class.java),
+ mock(NotificationSectionsFeatureManager::class.java),
+ personNotificationIdentifier
+ )
}
@Test
fun testPeopleNotification_isHighPriority() {
- val person = Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build()
-
val notification = Notification.Builder(mContext, "test")
- .addPerson(person)
.build()
val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
notification, mContext.user, "", 0)
+ `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
+
val e = NotificationEntryBuilder()
.setNotification(notification)
.setSbn(sbn)
@@ -97,27 +94,9 @@
}
@Test
- fun messagingStyleHighPriority() {
-
- val notif = Notification.Builder(mContext, "test")
- .setStyle(Notification.MessagingStyle(""))
- .build()
-
- val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
- notif, mContext.getUser(), "", 0)
-
- val e = NotificationEntryBuilder()
- .setNotification(notif)
- .setSbn(sbn)
- .build()
-
- assertTrue(rankingManager.isHighPriority2(e))
- }
-
- @Test
fun lowForegroundHighPriority() {
val notification = mock(Notification::class.java)
- `when`<Boolean>(notification.isForegroundService).thenReturn(true)
+ `when`(notification.isForegroundService).thenReturn(true)
val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
notification, mContext.user, "", 0)
@@ -136,15 +115,7 @@
@Test
fun userChangeTrumpsHighPriorityCharacteristics() {
- val person = Person.Builder()
- .setName("name")
- .setKey("abc")
- .setUri("uri")
- .setBot(true)
- .build()
-
val notification = Notification.Builder(mContext, "test")
- .addPerson(person)
.setStyle(Notification.MessagingStyle(""))
.setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
.build()
@@ -152,6 +123,8 @@
val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
notification, mContext.user, "", 0)
+ `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
+
val channel = NotificationChannel("a", "a", IMPORTANCE_LOW)
channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE)
@@ -297,14 +270,16 @@
headsUpManager: HeadsUpManager,
filter: NotificationFilter,
notifLog: NotifLog,
- sectionsFeatureManager: NotificationSectionsFeatureManager
+ sectionsFeatureManager: NotificationSectionsFeatureManager,
+ peopleNotificationIdentifier: PeopleNotificationIdentifier
) : NotificationRankingManager(
mediaManager,
groupManager,
headsUpManager,
filter,
notifLog,
- sectionsFeatureManager
+ sectionsFeatureManager,
+ peopleNotificationIdentifier
) {
fun isHighPriority2(e: NotificationEntry): Boolean {
@@ -315,4 +290,4 @@
rankingMap = r
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
new file mode 100644
index 0000000..87b3783d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.app.Notification.VISIBILITY_PUBLIC;
+import static android.app.Notification.VISIBILITY_SECRET;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardCoordinatorTest extends SysuiTestCase {
+ private static final int NOTIF_USER_ID = 0;
+ private static final int CURR_USER_ID = 1;
+
+ @Mock private Handler mMainHandler;
+ @Mock private KeyguardStateController mKeyguardStateController;
+ @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock private StatusBarStateController mStatusBarStateController;
+ @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+ private NotificationEntry mEntry;
+ private KeyguardCoordinator mKeyguardNotificationCoordinator;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mKeyguardNotificationCoordinator = new KeyguardCoordinator(
+ mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
+ mBroadcastDispatcher, mStatusBarStateController,
+ mKeyguardUpdateMonitor);
+
+ mEntry = new NotificationEntryBuilder()
+ .setUser(new UserHandle(NOTIF_USER_ID))
+ .build();
+ }
+
+ @Test
+ public void unfilteredState() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // THEN don't filter out the entry
+ assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void notificationNotForCurrentProfile() {
+ // GIVEN the notification isn't for the given user
+ setupUnfilteredState();
+ when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void keyguardNotShowing() {
+ // GIVEN the lockscreen isn't showing
+ setupUnfilteredState();
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+ // THEN don't filter out the entry
+ assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void doNotShowLockscreenNotifications() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN we shouldn't show any lockscreen notifications
+ when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void lockdown() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN the notification's user is in lockdown:
+ when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void publicMode_settingsDisallow() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN the notification's user is in public mode and settings are configured to disallow
+ // notifications in public mode
+ when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
+ when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
+ .thenReturn(false);
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void publicMode_notifDisallowed() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN the notification's user is in public mode and settings are configured to disallow
+ // notifications in public mode
+ when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setVisibilityOverride(VISIBILITY_SECRET).build());
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void doesNotExceedThresholdToShow() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN the notification doesn't exceed the threshold to show on the lockscreen
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setImportance(IMPORTANCE_MIN)
+ .build());
+
+ // THEN filter out the entry
+ assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ @Test
+ public void summaryExceedsThresholdToShow() {
+ // GIVEN an 'unfiltered-keyguard-showing' state
+ setupUnfilteredState();
+
+ // WHEN the notification doesn't exceed the threshold to show on the lockscreen
+ // but its summary does
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setImportance(IMPORTANCE_MIN)
+ .build());
+
+ final NotificationEntry summary = new NotificationEntryBuilder().build();
+ summary.setRanking(new RankingBuilder()
+ .setKey(summary.getKey())
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+ final GroupEntry group = new GroupEntry(mEntry.getSbn().getGroupKey());
+ group.setSummary(summary);
+ mEntry.setParent(group);
+
+ // THEN don't filter out the entry
+ assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+ }
+
+ /**
+ * setup a state where the notification will not be filtered by the
+ * KeyguardNotificationCoordinator when the keyguard is showing.
+ */
+ private void setupUnfilteredState() {
+ // notification is for current profile
+ when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true);
+
+ // keyguard is showing
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+ // show notifications on the lockscreen
+ when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true);
+
+ // neither the current user nor the notification's user is in lockdown
+ when(mLockscreenUserManager.getCurrentUserId()).thenReturn(CURR_USER_ID);
+ when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(false);
+ when(mKeyguardUpdateMonitor.isUserInLockdown(CURR_USER_ID)).thenReturn(false);
+
+ // not in public mode
+ when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(false);
+ when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(false);
+
+ // entry's ranking - should show on all lockscreens
+ // + priority of the notification exceeds the threshold to be shown on the lockscreen
+ mEntry.setRanking(new RankingBuilder()
+ .setKey(mEntry.getKey())
+ .setVisibilityOverride(VISIBILITY_PUBLIC)
+ .setImportance(IMPORTANCE_HIGH)
+ .build());
+
+ // settings allows notifications in public mode
+ when(mLockscreenUserManager.userAllowsNotificationsInPublic(CURR_USER_ID)).thenReturn(true);
+ when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
+ .thenReturn(true);
+
+ // notification doesn't have a summary
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index d20a37a..d2b4a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -74,6 +74,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.logging.NotifLog;
import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -164,7 +165,8 @@
mHeadsUpManager,
mock(NotificationFilter.class),
mock(NotifLog.class),
- mock(NotificationSectionsFeatureManager.class)
+ mock(NotificationSectionsFeatureManager.class),
+ mock(PeopleNotificationIdentifier.class)
),
mock(NotificationEntryManager.KeyguardEnvironment.class));
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 222fcb6..46f6cfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -85,7 +85,6 @@
@Mock private StatusBar mStatusBar;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private StatusBarWindowViewController mStatusBarWindowViewController;
- @Mock private StatusBarWindowView mStatusBarWindow;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock private NotificationPanelView mNotificationPanel;
@Mock private View mAmbientIndicationContainer;
@@ -101,10 +100,11 @@
mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler,
mStatusBarWindowController, mNotificationWakeUpCoordinator,
- mStatusBarWindowViewController, mLockscreenLockIconController);
+ mLockscreenLockIconController);
- mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, mStatusBarWindow,
- mStatusBarKeyguardViewManager, mNotificationPanel, mAmbientIndicationContainer);
+ mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController,
+ mStatusBarKeyguardViewManager, mStatusBarWindowViewController, mNotificationPanel,
+ mAmbientIndicationContainer);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 6f5cfbe..77cdc02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -102,7 +102,7 @@
@Mock
private RemoteInputController mRemoteInputController;
@Mock
- private ShadeController mShadeController;
+ private StatusBar mStatusBar;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
@@ -175,7 +175,7 @@
mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
mBubbleController, mSuperStatusBarViewFactory))
- .setShadeController(mShadeController)
+ .setStatusBar(mStatusBar)
.setNotificationPresenter(mock(NotificationPresenter.class))
.setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
.build();
@@ -186,11 +186,11 @@
// set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
doAnswer(answerVoid(Runnable::run))
- .when(mShadeController).addAfterKeyguardGoneRunnable(any(Runnable.class));
+ .when(mStatusBar).addAfterKeyguardGoneRunnable(any(Runnable.class));
// set up addPostCollapseAction to synchronously invoke the Runnable arg
doAnswer(answerVoid(Runnable::run))
- .when(mShadeController).addPostCollapseAction(any(Runnable.class));
+ .when(mStatusBar).addPostCollapseAction(any(Runnable.class));
// set up Handler to synchronously invoke the Runnable arg
doAnswer(answerVoid(Runnable::run))
@@ -209,13 +209,13 @@
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mShadeController.isOccluded()).thenReturn(true);
+ when(mStatusBar.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
// Then
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mStatusBar, atLeastOnce()).collapsePanel();
verify(mContentIntent).sendAndReturnResult(
any(Context.class),
@@ -250,7 +250,7 @@
verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
// This is called regardless, and simply short circuits when there is nothing to do.
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mStatusBar, atLeastOnce()).collapsePanel();
verify(mAssistManager).hideAssist();
@@ -272,7 +272,7 @@
// Given
sbn.getNotification().contentIntent = null;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mShadeController.isOccluded()).thenReturn(true);
+ when(mStatusBar.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -280,7 +280,7 @@
// Then
verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mStatusBar, atLeastOnce()).collapsePanel();
verify(mAssistManager).hideAssist();
@@ -302,7 +302,7 @@
// Given
sbn.getNotification().contentIntent = mContentIntent;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mShadeController.isOccluded()).thenReturn(true);
+ when(mStatusBar.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -310,7 +310,7 @@
// Then
verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
- verify(mShadeController, atLeastOnce()).collapsePanel();
+ verify(mStatusBar, atLeastOnce()).collapsePanel();
verify(mAssistManager).hideAssist();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1c02b60..14f5795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -71,10 +71,11 @@
public class StatusBarNotificationPresenterTest extends SysuiTestCase {
- private StatusBarNotificationPresenter mStatusBar;
+ private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
+ private StatusBar mStatusBar = mock(StatusBar.class);
@Before
public void setup() {
@@ -105,14 +106,14 @@
StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
- mStatusBar = new StatusBarNotificationPresenter(mContext,
+ mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
statusBarWindowView, mock(NotificationListContainerViewGroup.class),
mock(DozeScrimController.class), mock(ScrimController.class),
mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
mock(NotificationAlertingManager.class),
mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class),
- mCommandQueue);
+ mStatusBar, mCommandQueue);
}
@Test
@@ -129,7 +130,7 @@
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while disabled",
- mStatusBar.canHeadsUp(entry, entry.getSbn()));
+ mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
}
@Test
@@ -146,13 +147,13 @@
TestableLooper.get(this).processAllMessages();
assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
- mStatusBar.canHeadsUp(entry, entry.getSbn()));
+ mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
}
@Test
public void onActivatedMetrics() {
ActivatableNotificationView view = mock(ActivatableNotificationView.class);
- mStatusBar.onActivated(view);
+ mStatusBarNotificationPresenter.onActivated(view);
MetricsAsserts.assertHasLog("missing lockscreen note tap log",
mMetricsLogger.getLogs(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e78fb9e..be68097 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -145,6 +145,8 @@
import java.util.ArrayList;
import java.util.Optional;
+import javax.inject.Provider;
+
import dagger.Lazy;
@SmallTest
@@ -227,6 +229,9 @@
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
@Mock private Recents mRecents;
+ @Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
+ @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
+ @Mock private StatusBarComponent mStatusBarComponent;
@Mock private PluginManager mPluginManager;
@Mock private Divider mDivider;
@Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -300,6 +305,11 @@
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ when(mStatusBarComponentBuilderProvider.get()).thenReturn(mStatusBarComponentBuilder);
+ when(mStatusBarComponentBuilder.build()).thenReturn(mStatusBarComponent);
+ when(mStatusBarComponent.getStatusBarWindowViewController()).thenReturn(
+ mStatusBarWindowViewController);
+
mStatusBar = new StatusBar(
mContext,
mFeatureFlags,
@@ -354,7 +364,6 @@
mNotificationListener,
configurationController,
mStatusBarWindowController,
- mStatusBarWindowViewController,
mLockscreenLockIconController,
mDozeParameters,
mScrimController,
@@ -367,6 +376,7 @@
mVolumeComponent,
mCommandQueue,
Optional.of(mRecents),
+ mStatusBarComponentBuilderProvider,
mPluginManager,
mRemoteInputUriController,
Optional.of(mDivider),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index ee9ea9f..529333e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -36,7 +36,6 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -74,7 +73,6 @@
@Mock private StatusBar mStatusBar;
@Mock private DozeLog mDozeLog;
@Mock private DozeParameters mDozeParameters;
- @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@Mock private DockManager mDockManager;
@Before
@@ -85,7 +83,6 @@
when(mStatusBar.isDozing()).thenReturn(false);
mDependency.injectTestDependency(ShadeController.class, mShadeController);
- when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(mView);
when(mDockManager.isDocked()).thenReturn(false);
mController = new StatusBarWindowViewController(
@@ -105,9 +102,9 @@
mDozeLog,
mDozeParameters,
new CommandQueue(mContext),
- mSuperStatusBarViewFactory,
() -> mShadeController,
- mDockManager);
+ mDockManager,
+ mView);
mController.setupExpandedStatusBar();
mController.setService(mStatusBar);
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index ab74caa..95b055c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -500,7 +500,7 @@
public void testUpdateDataNetworkName() {
setupDefaultSignal();
String newDataName = "TestDataName";
- when(mServiceState.getDataOperatorAlphaShort()).thenReturn(newDataName);
+ when(mServiceState.getOperatorAlphaShort()).thenReturn(newDataName);
updateServiceState();
assertDataNetworkNameEquals(newDataName);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
new file mode 100644
index 0000000..3ed6c5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.FakeSystemClock.ClockTickListener;
+
+import java.util.Collections;
+import java.util.PriorityQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class FakeExecutor implements DelayableExecutor {
+ private final FakeSystemClock mClock;
+ private PriorityQueue<QueuedRunnable> mQueuedRunnables = new PriorityQueue<>();
+ private boolean mIgnoreClockUpdates;
+
+ private ClockTickListener mClockTickListener = new ClockTickListener() {
+ @Override
+ public void onUptimeMillis(long uptimeMillis) {
+ if (!mIgnoreClockUpdates) {
+ runAllReady();
+ }
+ }
+ };
+
+ /**
+ * Initializes a fake executor.
+ *
+ * @param clock FakeSystemClock allowing control over delayed runnables. It is strongly
+ * recommended that this clock have its auto-increment setting set to false to
+ * prevent unexpected advancement of the time.
+ */
+ public FakeExecutor(FakeSystemClock clock) {
+ mClock = clock;
+ mClock.addListener(mClockTickListener);
+ }
+
+ /**
+ * Runs a single runnable if it's scheduled to run according to the internal clock.
+ *
+ * If constructed to advance the clock automatically, this will advance the clock enough to
+ * run the next pending item.
+ *
+ * This method does not advance the clock past the item that was run.
+ *
+ * @return Returns true if an item was run.
+ */
+ public boolean runNextReady() {
+ if (!mQueuedRunnables.isEmpty() && mQueuedRunnables.peek().mWhen <= mClock.uptimeMillis()) {
+ mQueuedRunnables.poll().mRunnable.run();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Runs all Runnables that are scheduled to run according to the internal clock.
+ *
+ * If constructed to advance the clock automatically, this will advance the clock enough to
+ * run all the pending items. This method does not advance the clock past items that were
+ * run. It is equivalent to calling {@link #runNextReady()} in a loop.
+ *
+ * @return Returns the number of items that ran.
+ */
+ public int runAllReady() {
+ int num = 0;
+ while (runNextReady()) {
+ num++;
+ }
+
+ return num;
+ }
+
+ /**
+ * Advances the internal clock to the next item to run.
+ *
+ * The clock will only move forward. If the next item is set to run in the past or there is no
+ * next item, the clock does not change.
+ *
+ * Note that this will cause one or more items to actually run.
+ *
+ * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
+ */
+ public long advanceClockToNext() {
+ if (mQueuedRunnables.isEmpty()) {
+ return 0;
+ }
+
+ long startTime = mClock.uptimeMillis();
+ long nextTime = mQueuedRunnables.peek().mWhen;
+ if (nextTime <= startTime) {
+ return 0;
+ }
+ updateClock(nextTime);
+
+ return nextTime - startTime;
+ }
+
+
+ /**
+ * Advances the internal clock to the last item to run.
+ *
+ * The clock will only move forward. If the last item is set to run in the past or there is no
+ * next item, the clock does not change.
+ *
+ * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
+ */
+ public long advanceClockToLast() {
+ if (mQueuedRunnables.isEmpty()) {
+ return 0;
+ }
+
+ long startTime = mClock.uptimeMillis();
+ long nextTime = Collections.max(mQueuedRunnables).mWhen;
+ if (nextTime <= startTime) {
+ return 0;
+ }
+
+ updateClock(nextTime);
+
+ return nextTime - startTime;
+ }
+
+ /**
+ * Returns the number of un-executed runnables waiting to run.
+ */
+ public int numPending() {
+ return mQueuedRunnables.size();
+ }
+
+ @Override
+ public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
+ if (delay < 0) {
+ delay = 0;
+ }
+ return executeAtTime(r, mClock.uptimeMillis() + unit.toMillis(delay));
+ }
+
+ @Override
+ public Runnable executeAtTime(Runnable r, long uptime, TimeUnit unit) {
+ long uptimeMillis = unit.toMillis(uptime);
+
+ QueuedRunnable container = new QueuedRunnable(r, uptimeMillis);
+
+ mQueuedRunnables.offer(container);
+
+ return () -> mQueuedRunnables.remove(container);
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ executeDelayed(command, 0);
+ }
+
+ @Override
+ public void removeAll() {
+ mQueuedRunnables.clear();
+ }
+
+ /**
+ * Run all Executors in a loop until they all report they have no ready work to do.
+ *
+ * Useful if you have Executors the post work to other Executors, and you simply want to
+ * run them all until they stop posting work.
+ */
+ public static void exhaustExecutors(FakeExecutor ...executors) {
+ boolean didAnything;
+ do {
+ didAnything = false;
+ for (FakeExecutor executor : executors) {
+ didAnything = didAnything || executor.runAllReady() != 0;
+ }
+ } while (didAnything);
+ }
+
+ private void updateClock(long nextTime) {
+ mIgnoreClockUpdates = true;
+ mClock.setUptimeMillis(nextTime);
+ mIgnoreClockUpdates = false;
+ }
+
+ private static class QueuedRunnable implements Comparable<QueuedRunnable> {
+ private static AtomicInteger sCounter = new AtomicInteger();
+
+ Runnable mRunnable;
+ long mWhen;
+ private int mCounter;
+
+ private QueuedRunnable(Runnable r, long when) {
+ mRunnable = r;
+ mWhen = when;
+
+ // PrioirityQueue orders items arbitrarily when equal. We want to ensure that
+ // otherwise-equal elements are ordered according to their insertion order. Because this
+ // class only is constructed right before insertion, we use a static counter to track
+ // insertion order of otherwise equal elements.
+ mCounter = sCounter.incrementAndGet();
+ }
+
+ @Override
+ public int compareTo(QueuedRunnable other) {
+ long diff = mWhen - other.mWhen;
+
+ if (diff == 0) {
+ return mCounter - other.mCounter;
+ }
+
+ return diff > 0 ? 1 : -1;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
new file mode 100644
index 0000000..7fd69424
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import kotlin.jvm.functions.Function4;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeExecutorTest extends SysuiTestCase {
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ /**
+ * Test FakeExecutor that receives non-delayed items to execute.
+ */
+ @Test
+ public void testNoDelay() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnable = new RunnableImpl();
+
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(0, runnable.mRunCount);
+
+ // Execute two runnables. They should not run and should be left pending.
+ fakeExecutor.execute(runnable);
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(1, fakeExecutor.numPending());
+ fakeExecutor.execute(runnable);
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(2, fakeExecutor.numPending());
+
+ // Run one pending runnable.
+ assertTrue(fakeExecutor.runNextReady());
+ assertEquals(1, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(1, fakeExecutor.numPending());
+ // Run a second pending runnable.
+ assertTrue(fakeExecutor.runNextReady());
+ assertEquals(2, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(0, fakeExecutor.numPending());
+
+ // No more runnables to run.
+ assertFalse(fakeExecutor.runNextReady());
+
+ // Add two more runnables.
+ fakeExecutor.execute(runnable);
+ fakeExecutor.execute(runnable);
+ assertEquals(2, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(2, fakeExecutor.numPending());
+ // Execute all pending runnables in batch.
+ assertEquals(2, fakeExecutor.runAllReady());
+ assertEquals(4, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(0, fakeExecutor.runAllReady());
+ }
+
+ /**
+ * Test FakeExecutor that is told to delay execution on items.
+ */
+ @Test
+ public void testDelayed() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnable = new RunnableImpl();
+
+ // Add three delayed runnables.
+ fakeExecutor.executeDelayed(runnable, 1);
+ fakeExecutor.executeDelayed(runnable, 50);
+ fakeExecutor.executeDelayed(runnable, 100);
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(3, fakeExecutor.numPending());
+ // Delayed runnables should not advance the clock and therefore should not run.
+ assertFalse(fakeExecutor.runNextReady());
+ assertEquals(0, fakeExecutor.runAllReady());
+ assertEquals(3, fakeExecutor.numPending());
+
+ // Advance the clock to the next runnable. One runnable should execute.
+ assertEquals(1, fakeExecutor.advanceClockToNext());
+ assertEquals(1, fakeExecutor.runAllReady());
+ assertEquals(2, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+ // Advance the clock to the last runnable.
+ assertEquals(99, fakeExecutor.advanceClockToLast());
+ assertEquals(2, fakeExecutor.runAllReady());
+ // Now all remaining runnables should execute.
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(3, runnable.mRunCount);
+ }
+
+ /**
+ * Test FakeExecutor that is told to delay execution on items.
+ */
+ @Test
+ public void testDelayed_AdvanceAndRun() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnable = new RunnableImpl();
+
+ // Add three delayed runnables.
+ fakeExecutor.executeDelayed(runnable, 1);
+ fakeExecutor.executeDelayed(runnable, 50);
+ fakeExecutor.executeDelayed(runnable, 100);
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, clock.uptimeMillis());
+ assertEquals(3, fakeExecutor.numPending());
+ // Delayed runnables should not advance the clock and therefore should not run.
+ assertFalse(fakeExecutor.runNextReady());
+ assertEquals(0, fakeExecutor.runAllReady());
+ assertEquals(3, fakeExecutor.numPending());
+
+ // Advance the clock to the next runnable. Check that it is run.
+ assertEquals(1, fakeExecutor.advanceClockToNext());
+ assertEquals(1, fakeExecutor.runAllReady());
+ assertEquals(1, clock.uptimeMillis());
+ assertEquals(2, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+ assertEquals(49, fakeExecutor.advanceClockToNext());
+ assertEquals(1, fakeExecutor.runAllReady());
+ assertEquals(50, clock.uptimeMillis());
+ assertEquals(1, fakeExecutor.numPending());
+ assertEquals(2, runnable.mRunCount);
+ assertEquals(50, fakeExecutor.advanceClockToNext());
+ assertEquals(1, fakeExecutor.runAllReady());
+ assertEquals(100, clock.uptimeMillis());
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(3, runnable.mRunCount);
+
+ // Nothing left to do
+ assertEquals(0, fakeExecutor.advanceClockToNext());
+ assertEquals(0, fakeExecutor.runAllReady());
+ assertEquals(100, clock.uptimeMillis());
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(3, runnable.mRunCount);
+ }
+
+ /**
+ * Test execution order.
+ */
+ @Test
+ public void testExecutionOrder() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnableA = new RunnableImpl();
+ RunnableImpl runnableB = new RunnableImpl();
+ RunnableImpl runnableC = new RunnableImpl();
+ RunnableImpl runnableD = new RunnableImpl();
+
+ Function4<Integer, Integer, Integer, Integer, Void> checkRunCounts =
+ (Integer argA, Integer argB, Integer argC, Integer argD) -> {
+ assertEquals("RunnableA run count wrong", argA.intValue(), runnableA.mRunCount);
+ assertEquals("RunnableB run count wrong", argB.intValue(), runnableB.mRunCount);
+ assertEquals("RunnableC run count wrong", argC.intValue(), runnableC.mRunCount);
+ assertEquals("RunnableD run count wrong", argD.intValue(), runnableD.mRunCount);
+ return null;
+ };
+
+ assertEquals(0, clock.uptimeMillis());
+ checkRunCounts.invoke(0, 0, 0, 0);
+
+ fakeExecutor.execute(runnableA);
+ fakeExecutor.execute(runnableB);
+ fakeExecutor.execute(runnableC);
+ fakeExecutor.execute(runnableD);
+
+ fakeExecutor.runNextReady();
+ checkRunCounts.invoke(1, 0, 0, 0);
+ fakeExecutor.runNextReady();
+ checkRunCounts.invoke(1, 1, 0, 0);
+ fakeExecutor.runNextReady();
+ checkRunCounts.invoke(1, 1, 1, 0);
+ fakeExecutor.runNextReady();
+ checkRunCounts.invoke(1, 1, 1, 1);
+
+ fakeExecutor.executeDelayed(runnableA, 100);
+ fakeExecutor.execute(runnableB);
+ fakeExecutor.executeDelayed(runnableC, 50);
+ fakeExecutor.execute(runnableD);
+
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(1, 2, 1, 2);
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(1, 2, 2, 2);
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(2, 2, 2, 2);
+
+ fakeExecutor.execute(runnableA);
+ fakeExecutor.executeAtTime(runnableB, 0); // this is in the past!
+ fakeExecutor.executeAtTime(runnableC, 1000);
+ fakeExecutor.executeAtTime(runnableD, 500);
+
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(3, 3, 2, 2);
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(3, 3, 2, 3);
+ fakeExecutor.advanceClockToNext();
+ fakeExecutor.runAllReady();
+ checkRunCounts.invoke(3, 3, 3, 3);
+ }
+
+ /**
+ * Test removing a single item.
+ */
+ @Test
+ public void testRemoval_single() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnable = new RunnableImpl();
+ Runnable removeFunction;
+
+ // Nothing to remove.
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, fakeExecutor.numPending());
+
+ // Two pending items that have not yet run.
+ // We will try to remove the second item.
+ fakeExecutor.executeDelayed(runnable, 100);
+ removeFunction = fakeExecutor.executeDelayed(runnable, 200);
+ assertEquals(2, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+
+ // Remove the item.
+ removeFunction.run();
+ assertEquals(1, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+
+ // One item to run.
+ fakeExecutor.advanceClockToLast();
+ fakeExecutor.runAllReady();
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+
+ // Nothing to remove.
+ removeFunction.run();
+ fakeExecutor.runAllReady();
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+ }
+
+ /**
+ * Test removing multiple items.
+ */
+ @Test
+ public void testRemoval_multi() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ List<Runnable> removeFunctions = new ArrayList<>();
+ RunnableImpl runnable = new RunnableImpl();
+
+ // Nothing to remove.
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, fakeExecutor.numPending());
+
+ // Three pending items that have not yet run.
+ // We will try to remove the first and third items.
+ removeFunctions.add(fakeExecutor.executeDelayed(runnable, 100));
+ fakeExecutor.executeDelayed(runnable, 200);
+ removeFunctions.add(fakeExecutor.executeDelayed(runnable, 300));
+ assertEquals(3, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+
+ // Remove the items.
+ removeFunctions.forEach(Runnable::run);
+ assertEquals(1, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+
+ // One item to run.
+ fakeExecutor.advanceClockToLast();
+ fakeExecutor.runAllReady();
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+
+ // Nothing to remove.
+ removeFunctions.forEach(Runnable::run);
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(1, runnable.mRunCount);
+ }
+
+ /**
+ * Test removing everything
+ */
+ @Test
+ public void testRemoval_all() {
+ FakeSystemClock clock = new FakeSystemClock();
+ clock.setAutoIncrement(false);
+ FakeExecutor fakeExecutor = new FakeExecutor(clock);
+ RunnableImpl runnable = new RunnableImpl();
+
+ // Nothing to remove.
+ assertEquals(0, runnable.mRunCount);
+ assertEquals(0, fakeExecutor.numPending());
+
+ // Two pending items that have not yet run.
+ fakeExecutor.executeDelayed(runnable, 100);
+ fakeExecutor.executeDelayed(runnable, 200);
+ assertEquals(2, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+
+ // Remove the items.
+ fakeExecutor.removeAll();
+
+ // Nothing to run
+ fakeExecutor.advanceClockToLast();
+ assertEquals(0, fakeExecutor.runAllReady());
+ assertEquals(0, fakeExecutor.numPending());
+ assertEquals(0, runnable.mRunCount);
+ }
+
+ private static class RunnableImpl implements Runnable {
+ int mRunCount;
+
+ @Override
+ public void run() {
+ mRunCount++;
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index 7b5417c..65e5902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -16,6 +16,9 @@
package com.android.systemui.util.time;
+import java.util.ArrayList;
+import java.util.List;
+
public class FakeSystemClock implements SystemClock {
private boolean mAutoIncrement = true;
@@ -26,11 +29,13 @@
private long mCurrentThreadTimeMicro;
private long mCurrentTimeMicro;
+ List<ClockTickListener> mListeners = new ArrayList<>();
+
@Override
public long uptimeMillis() {
long value = mUptimeMillis;
if (mAutoIncrement) {
- mUptimeMillis++;
+ setUptimeMillis(mUptimeMillis + 1);
}
return value;
}
@@ -39,7 +44,7 @@
public long elapsedRealtime() {
long value = mElapsedRealtime;
if (mAutoIncrement) {
- mElapsedRealtime++;
+ setElapsedRealtime(mElapsedRealtime + 1);
}
return value;
}
@@ -48,7 +53,7 @@
public long elapsedRealtimeNanos() {
long value = mElapsedRealtimeNanos;
if (mAutoIncrement) {
- mElapsedRealtimeNanos++;
+ setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1);
}
return value;
}
@@ -57,7 +62,7 @@
public long currentThreadTimeMillis() {
long value = mCurrentThreadTimeMillis;
if (mAutoIncrement) {
- mCurrentThreadTimeMillis++;
+ setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1);
}
return value;
}
@@ -66,7 +71,7 @@
public long currentThreadTimeMicro() {
long value = mCurrentThreadTimeMicro;
if (mAutoIncrement) {
- mCurrentThreadTimeMicro++;
+ setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1);
}
return value;
}
@@ -75,37 +80,90 @@
public long currentTimeMicro() {
long value = mCurrentTimeMicro;
if (mAutoIncrement) {
- mCurrentTimeMicro++;
+ setCurrentTimeMicro(mCurrentTimeMicro + 1);
}
return value;
}
public void setUptimeMillis(long uptimeMillis) {
mUptimeMillis = uptimeMillis;
+ for (ClockTickListener listener : mListeners) {
+ listener.onUptimeMillis(mUptimeMillis);
+ }
}
public void setElapsedRealtime(long elapsedRealtime) {
mElapsedRealtime = elapsedRealtime;
+ for (ClockTickListener listener : mListeners) {
+ listener.onElapsedRealtime(mElapsedRealtime);
+ }
}
public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ for (ClockTickListener listener : mListeners) {
+ listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
+ }
}
public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) {
mCurrentThreadTimeMillis = currentThreadTimeMillis;
+ for (ClockTickListener listener : mListeners) {
+ listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
+ }
}
public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) {
mCurrentThreadTimeMicro = currentThreadTimeMicro;
+ for (ClockTickListener listener : mListeners) {
+ listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
+ }
}
public void setCurrentTimeMicro(long currentTimeMicro) {
mCurrentTimeMicro = currentTimeMicro;
+ for (ClockTickListener listener : mListeners) {
+ listener.onCurrentTimeMicro(mCurrentTimeMicro);
+ }
}
/** If true, each call to get____ will be one higher than the previous call to that method. */
public void setAutoIncrement(boolean autoIncrement) {
mAutoIncrement = autoIncrement;
}
+
+ public void addListener(ClockTickListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeListener(ClockTickListener listener) {
+ mListeners.remove(listener);
+ }
+
+ /** Alert all the listeners about the current time. */
+ public void synchronizeListeners() {
+ for (ClockTickListener listener : mListeners) {
+ listener.onUptimeMillis(mUptimeMillis);
+ listener.onElapsedRealtime(mElapsedRealtime);
+ listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
+ listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
+ listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
+ listener.onCurrentTimeMicro(mCurrentTimeMicro);
+ }
+ }
+
+
+ public interface ClockTickListener {
+ default void onUptimeMillis(long uptimeMillis) {}
+
+ default void onElapsedRealtime(long elapsedRealtime) {}
+
+ default void onElapsedRealtimeNanos(long elapsedRealtimeNanos) {}
+
+ default void onCurrentThreadTimeMillis(long currentThreadTimeMillis) {}
+
+ default void onCurrentThreadTimeMicro(long currentThreadTimeMicro) {}
+
+ default void onCurrentTimeMicro(long currentTimeMicro) {}
+ }
}
diff --git a/services/Android.bp b/services/Android.bp
index 041631b..3b56607 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -96,3 +96,8 @@
name: "services-platform-compat-config",
src: ":services",
}
+
+filegroup {
+ name: "art-profile",
+ srcs: ["art-profile"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 339fc96..8ce92a3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -972,6 +972,9 @@
if (shouldComputeWindows) {
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
}
+
+ mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
+ windowToken.asBinder(), windowId);
return windowId;
}
@@ -991,7 +994,7 @@
final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked(
token, mGlobalWindowTokens, mGlobalInteractionConnections);
if (removedWindowId >= 0) {
- onAccessibilityInteractionConnectionRemovedLocked(removedWindowId);
+ onAccessibilityInteractionConnectionRemovedLocked(removedWindowId, token);
if (DEBUG) {
Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid()
+ " with windowId: " + removedWindowId + " and token: "
@@ -1007,7 +1010,8 @@
getWindowTokensForUserLocked(userId),
getInteractionConnectionsForUserLocked(userId));
if (removedWindowIdForUser >= 0) {
- onAccessibilityInteractionConnectionRemovedLocked(removedWindowIdForUser);
+ onAccessibilityInteractionConnectionRemovedLocked(
+ removedWindowIdForUser, token);
if (DEBUG) {
Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid()
+ " with windowId: " + removedWindowIdForUser + " and userId:"
@@ -1069,18 +1073,21 @@
* @param userId The userId to remove
*/
private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) {
+ IBinder window = null;
if (userId == UserHandle.USER_ALL) {
+ window = mGlobalWindowTokens.get(windowId);
mGlobalWindowTokens.remove(windowId);
mGlobalInteractionConnections.remove(windowId);
} else {
if (isValidUserForWindowTokensLocked(userId)) {
+ window = getWindowTokensForUserLocked(userId).get(windowId);
getWindowTokensForUserLocked(userId).remove(windowId);
}
if (isValidUserForInteractionConnectionsLocked(userId)) {
getInteractionConnectionsForUserLocked(userId).remove(windowId);
}
}
- onAccessibilityInteractionConnectionRemovedLocked(windowId);
+ onAccessibilityInteractionConnectionRemovedLocked(windowId, window);
if (DEBUG) {
Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
}
@@ -1091,12 +1098,17 @@
*
* @param windowId Removed windowId
*/
- private void onAccessibilityInteractionConnectionRemovedLocked(int windowId) {
+ private void onAccessibilityInteractionConnectionRemovedLocked(
+ int windowId, @Nullable IBinder binder) {
// Active window will not update, if windows callback is unregistered.
// Update active window to invalid, when its a11y interaction connection is removed.
if (!isTrackingWindowsLocked() && windowId >= 0 && mActiveWindowId == windowId) {
mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
}
+ if (binder != null) {
+ mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
+ binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
+ }
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 19ac0d3..1754926 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -17,6 +17,8 @@
package com.android.server.accessibility;
import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
import android.app.StatusBarManager;
import android.content.Context;
import android.hardware.input.InputManager;
@@ -25,81 +27,272 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Slog;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ScreenshotHelper;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import java.util.function.Supplier;
/**
- * Handle the back-end of AccessibilityService#performGlobalAction
+ * Handle the back-end of system AccessibilityAction.
+ *
+ * This class should support three use cases with combined usage of new API and legacy API:
+ *
+ * Use case 1: SystemUI doesn't use the new system action registration API. Accessibility
+ * service doesn't use the new system action API to obtain action list. Accessibility
+ * service uses legacy global action id to perform predefined system actions.
+ * Use case 2: SystemUI uses the new system action registration API to register available system
+ * actions. Accessibility service doesn't use the new system action API to obtain action
+ * list. Accessibility service uses legacy global action id to trigger the system
+ * actions registered by SystemUI.
+ * Use case 3: SystemUI doesn't use the new system action registration API.Accessibility service
+ * obtains the available system actions using new AccessibilityService API and trigger
+ * the predefined system actions.
*/
public class SystemActionPerformer {
+ private static final String TAG = "SystemActionPerformer";
+
+ interface SystemActionsChangedListener {
+ void onSystemActionsChanged();
+ }
+ private final SystemActionsChangedListener mListener;
+
+ private final Object mSystemActionLock = new Object();
+ // Resource id based ActionId -> RemoteAction
+ @GuardedBy("mSystemActionLock")
+ private final Map<Integer, RemoteAction> mRegisteredSystemActions = new ArrayMap<>();
+
+ // Legacy system actions.
+ private final AccessibilityAction mLegacyHomeAction;
+ private final AccessibilityAction mLegacyBackAction;
+ private final AccessibilityAction mLegacyRecentsAction;
+ private final AccessibilityAction mLegacyNotificationsAction;
+ private final AccessibilityAction mLegacyQuickSettingsAction;
+ private final AccessibilityAction mLegacyPowerDialogAction;
+ private final AccessibilityAction mLegacyToggleSplitScreenAction;
+ private final AccessibilityAction mLegacyLockScreenAction;
+ private final AccessibilityAction mLegacyTakeScreenshotAction;
+
private final WindowManagerInternal mWindowManagerService;
private final Context mContext;
private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
- public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
- mContext = context;
- mWindowManagerService = windowManagerInternal;
- mScreenshotHelperSupplier = null;
+ public SystemActionPerformer(
+ Context context,
+ WindowManagerInternal windowManagerInternal) {
+ this(context, windowManagerInternal, null, null);
}
// Used to mock ScreenshotHelper
@VisibleForTesting
- public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+ public SystemActionPerformer(
+ Context context,
+ WindowManagerInternal windowManagerInternal,
Supplier<ScreenshotHelper> screenshotHelperSupplier) {
- this(context, windowManagerInternal);
+ this(context, windowManagerInternal, screenshotHelperSupplier, null);
+ }
+
+ public SystemActionPerformer(
+ Context context,
+ WindowManagerInternal windowManagerInternal,
+ Supplier<ScreenshotHelper> screenshotHelperSupplier,
+ SystemActionsChangedListener listener) {
+ mContext = context;
+ mWindowManagerService = windowManagerInternal;
+ mListener = listener;
mScreenshotHelperSupplier = screenshotHelperSupplier;
+
+ mLegacyHomeAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_HOME,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_home_label));
+ mLegacyBackAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_BACK,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_back_label));
+ mLegacyRecentsAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_RECENTS,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_recents_label));
+ mLegacyNotificationsAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_notifications_label));
+ mLegacyQuickSettingsAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_quick_settings_label));
+ mLegacyPowerDialogAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_POWER_DIALOG,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_power_dialog_label));
+ mLegacyToggleSplitScreenAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_toggle_split_screen_label));
+ mLegacyLockScreenAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_lock_screen_label));
+ mLegacyTakeScreenshotAction = new AccessibilityAction(
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT,
+ mContext.getResources().getString(
+ R.string.accessibility_system_action_screenshot_label));
}
/**
- * Performe the system action matching the given action id.
+ * This method is called to register a system action. If a system action is already registered
+ * with the given id, the existing system action will be overwritten.
*/
- public boolean performSystemAction(int action) {
+ void registerSystemAction(int id, RemoteAction action) {
+ synchronized (mSystemActionLock) {
+ mRegisteredSystemActions.put(id, action);
+ }
+ if (mListener != null) {
+ mListener.onSystemActionsChanged();
+ }
+ }
+
+ /**
+ * This method is called to unregister a system action previously registered through
+ * registerSystemAction.
+ */
+ void unregisterSystemAction(int id) {
+ synchronized (mSystemActionLock) {
+ mRegisteredSystemActions.remove(id);
+ }
+ if (mListener != null) {
+ mListener.onSystemActionsChanged();
+ }
+ }
+
+ /**
+ * This method returns the list of available system actions.
+ */
+ List<AccessibilityAction> getSystemActions() {
+ List<AccessibilityAction> systemActions = new ArrayList<>();
+ synchronized (mSystemActionLock) {
+ for (Map.Entry<Integer, RemoteAction> entry : mRegisteredSystemActions.entrySet()) {
+ AccessibilityAction systemAction = new AccessibilityAction(
+ entry.getKey(),
+ entry.getValue().getTitle());
+ systemActions.add(systemAction);
+ }
+
+ // add AccessibilitySystemAction entry for legacy system actions if not overwritten
+ addLegacySystemActions(systemActions);
+ }
+ return systemActions;
+ }
+
+ private void addLegacySystemActions(List<AccessibilityAction> systemActions) {
+ if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_BACK)) {
+ systemActions.add(mLegacyBackAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_HOME)) {
+ systemActions.add(mLegacyHomeAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_RECENTS)) {
+ systemActions.add(mLegacyRecentsAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS)) {
+ systemActions.add(mLegacyNotificationsAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS)) {
+ systemActions.add(mLegacyQuickSettingsAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)) {
+ systemActions.add(mLegacyPowerDialogAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) {
+ systemActions.add(mLegacyToggleSplitScreenAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) {
+ systemActions.add(mLegacyLockScreenAction);
+ }
+ if (!mRegisteredSystemActions.containsKey(
+ AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)) {
+ systemActions.add(mLegacyTakeScreenshotAction);
+ }
+ }
+
+ /**
+ * Trigger the registered action by the matching action id.
+ */
+ public boolean performSystemAction(int actionId) {
final long identity = Binder.clearCallingIdentity();
try {
- switch (action) {
- case AccessibilityService.GLOBAL_ACTION_BACK: {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_HOME: {
- sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_RECENTS: {
- return openRecents();
- }
- case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
- expandNotifications();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
- expandQuickSettings();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
- showGlobalActions();
- }
- return true;
- case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
- return toggleSplitScreen();
- }
- case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
- return lockScreen();
- }
- case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
- return takeScreenshot();
+ synchronized (mSystemActionLock) {
+ // If a system action is registered with the given actionId, call the corresponding
+ // RemoteAction.
+ RemoteAction registeredAction = mRegisteredSystemActions.get(actionId);
+ if (registeredAction != null) {
+ try {
+ registeredAction.getActionIntent().send();
+ return true;
+ } catch (PendingIntent.CanceledException ex) {
+ Slog.e(TAG,
+ "canceled PendingIntent for global action "
+ + registeredAction.getTitle(),
+ ex);
+ }
+ return false;
}
}
- return false;
+
+ // No RemoteAction registered with the given actionId, try the default legacy system
+ // actions.
+ switch (actionId) {
+ case AccessibilityService.GLOBAL_ACTION_BACK: {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
+ return true;
+ }
+ case AccessibilityService.GLOBAL_ACTION_HOME: {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+ return true;
+ }
+ case AccessibilityService.GLOBAL_ACTION_RECENTS:
+ return openRecents();
+ case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
+ expandNotifications();
+ return true;
+ }
+ case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
+ expandQuickSettings();
+ return true;
+ }
+ case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
+ showGlobalActions();
+ return true;
+ }
+ case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
+ return toggleSplitScreen();
+ case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
+ return lockScreen();
+ case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
+ return takeScreenshot();
+ default:
+ return false;
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 6641b5b..2f8c506 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -281,4 +281,13 @@
return mUsageRemaining;
}
}
+
+ /**
+ * Called by {@link com.android.server.usage.UsageStatsIdleService} when the device is idle to
+ * prune usage stats data for uninstalled packages.
+ *
+ * @param userId the user associated with the job
+ * @return {@code true} if the pruning was successful, {@code false} otherwise
+ */
+ public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0ad275f..753c117 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -6464,20 +6464,77 @@
newNetwork.name(), score, newNetwork.getCurrentScore()));
}
+ // Notify requested networks are available after the default net is switched, but
+ // before LegacyTypeTracker sends legacy broadcasts
+ for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
+
// Second pass: process all listens.
if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
- // If the network went from background to foreground or vice versa, we need to update
- // its foreground state. It is safe to do this after rematching the requests because
- // NET_CAPABILITY_FOREGROUND does not affect requests, as is not a requestable
- // capability and does not affect the network's score (see the Slog.wtf call above).
- updateCapabilities(score, newNetwork, newNetwork.networkCapabilities);
+ // TODO : most of the following is useless because the only thing that changed
+ // here is whether the network is a background network. Clean this up.
+
+ NetworkCapabilities newNc = mixInCapabilities(newNetwork,
+ newNetwork.networkCapabilities);
+
+ if (Objects.equals(newNetwork.networkCapabilities, newNc)) return;
+
+ final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
+ final int newPermission = getNetworkPermission(newNc);
+ if (oldPermission != newPermission && newNetwork.created && !newNetwork.isVPN()) {
+ try {
+ mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
+ } catch (RemoteException e) {
+ loge("Exception in setNetworkPermission: " + e);
+ }
+ }
+
+ final NetworkCapabilities prevNc;
+ synchronized (newNetwork) {
+ prevNc = newNetwork.networkCapabilities;
+ newNetwork.setNetworkCapabilities(newNc);
+ }
+
+ updateUids(newNetwork, prevNc, newNc);
+
+ if (newNetwork.getCurrentScore() == score
+ && newNc.equalRequestableCapabilities(prevNc)) {
+ // If the requestable capabilities haven't changed, and the score hasn't changed,
+ // then the change we're processing can't affect any requests, it can only affect
+ // the listens on this network.
+ processListenRequests(newNetwork, true);
+ } else {
+ rematchAllNetworksAndRequests();
+ notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
+ }
+
+ if (prevNc != null) {
+ final boolean oldMetered = prevNc.isMetered();
+ final boolean newMetered = newNc.isMetered();
+ final boolean meteredChanged = oldMetered != newMetered;
+
+ if (meteredChanged) {
+ maybeNotifyNetworkBlocked(newNetwork, oldMetered, newMetered,
+ mRestrictBackground, mRestrictBackground);
+ }
+
+ final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
+ != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ // Report changes that are interesting for network statistics tracking.
+ if (meteredChanged || roamingChanged) {
+ notifyIfacesChangedForNetworkStats();
+ }
+ }
+
+ if (!newNc.hasTransport(TRANSPORT_VPN)) {
+ // Tell VPNs about updated capabilities, since they may need to
+ // bubble those changes through.
+ updateAllVpnsCapabilities();
+ }
+
} else {
processListenRequests(newNetwork, false);
}
-
- // do this after the default net is switched, but
- // before LegacyTypeTracker sends legacy broadcasts
- for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
}
/**
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a9c38bc..76e0c13 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -4,3 +4,6 @@
# Vibrator / Threads
per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
per-file VibratorService.java, DisplayThread.java = ogunwale@google.com
+
+# Zram writeback
+per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index e2fe76e..12debbf 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -42,6 +42,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import libcore.io.IoUtils;
@@ -200,7 +201,8 @@
}
/**
- * Registers {@code observer} to listen for package failures
+ * Registers {@code observer} to listen for package failures. Add a new ObserverInternal for
+ * this observer if it does not already exist.
*
* <p>Observers are expected to call this on boot. It does not specify any packages but
* it will resume observing any packages requested from a previous boot.
@@ -210,6 +212,11 @@
ObserverInternal internalObserver = mAllObservers.get(observer.getName());
if (internalObserver != null) {
internalObserver.registeredObserver = observer;
+ } else {
+ internalObserver = new ObserverInternal(observer.getName(), new ArrayList<>());
+ internalObserver.registeredObserver = observer;
+ mAllObservers.put(observer.getName(), internalObserver);
+ syncState("added new observer");
}
}
}
@@ -415,6 +422,14 @@
* watchdog may drop observing packages with the old name.
*/
String getName();
+
+ /**
+ * An observer will not be pruned if this is set, even if the observer is not explicitly
+ * monitoring any packages.
+ */
+ default boolean isPersistent() {
+ return false;
+ }
}
long getTriggerFailureCount() {
@@ -626,7 +641,8 @@
if (!failedPackages.isEmpty()) {
onHealthCheckFailed(observer, failedPackages);
}
- if (observer.packages.isEmpty()) {
+ if (observer.packages.isEmpty() && (observer.registeredObserver == null
+ || !observer.registeredObserver.isPersistent())) {
Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired");
it.remove();
}
@@ -802,6 +818,21 @@
}
}
+ /** Dump status of every observer in mAllObservers. */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Package Watchdog status");
+ pw.increaseIndent();
+ synchronized (mLock) {
+ for (String observerName : mAllObservers.keySet()) {
+ pw.println("Observer name: " + observerName);
+ pw.increaseIndent();
+ ObserverInternal observerInternal = mAllObservers.get(observerName);
+ observerInternal.dump(pw);
+ pw.decreaseIndent();
+ }
+ }
+ }
+
/**
* Represents an observer monitoring a set of packages along with the failure thresholds for
* each package.
@@ -944,6 +975,22 @@
}
return new ObserverInternal(observerName, packages);
}
+
+ /** Dumps information about this observer and the packages it watches. */
+ public void dump(IndentingPrintWriter pw) {
+ boolean isPersistent = registeredObserver != null && registeredObserver.isPersistent();
+ pw.println("Persistent: " + isPersistent);
+ for (String packageName : packages.keySet()) {
+ MonitoredPackage p = packages.get(packageName);
+ pw.println(packageName + ": ");
+ pw.increaseIndent();
+ pw.println("# Failures: " + p.mFailureHistory.size());
+ pw.println("Monitoring duration remaining: " + p.mDurationMs + "ms");
+ pw.println("Explicit health check duration: " + p.mHealthCheckDurationMs + "ms");
+ pw.println("Health check state: " + p.toString(p.mHealthCheckState));
+ pw.decreaseIndent();
+ }
+ }
}
@Retention(SOURCE)
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 24cd21e..1f7d9ea 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -17,6 +17,8 @@
package com.android.server;
import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
+import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_DATA;
+import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_VOICE;
import static java.util.Arrays.copyOf;
@@ -51,7 +53,6 @@
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
import android.telephony.PhoneStateListener;
-import android.telephony.PhysicalChannelConfig;
import android.telephony.PreciseCallState;
import android.telephony.PreciseDataConnectionState;
import android.telephony.PreciseDisconnectCause;
@@ -214,8 +215,6 @@
private ArrayList<List<CellInfo>> mCellInfo = null;
- private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
-
private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
private EmergencyNumber[] mOutgoingSmsEmergencyNumber;
@@ -425,7 +424,6 @@
if (mNumPhones < oldNumPhones) {
cutListToSize(mCellInfo, mNumPhones);
cutListToSize(mImsReasonInfo, mNumPhones);
- cutListToSize(mPhysicalChannelConfigs, mNumPhones);
return;
}
@@ -446,7 +444,6 @@
mCellInfo.add(i, null);
mImsReasonInfo.add(i, null);
mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
- mPhysicalChannelConfigs.add(i, new ArrayList<>());
mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
@@ -523,7 +520,6 @@
mPreciseDataConnectionState = new PreciseDataConnectionState[numPhones];
mCellInfo = new ArrayList<>();
mImsReasonInfo = new ArrayList<>();
- mPhysicalChannelConfigs = new ArrayList<>();
mEmergencyNumberList = new HashMap<>();
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
@@ -543,7 +539,6 @@
mCellInfo.add(i, null);
mImsReasonInfo.add(i, null);
mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
- mPhysicalChannelConfigs.add(i, new ArrayList<>());
mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
@@ -962,14 +957,6 @@
remove(r.binder);
}
}
- if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
- try {
- r.callback.onPhysicalChannelConfigurationChanged(
- mPhysicalChannelConfigs.get(phoneId));
- } catch (RemoteException ex) {
- remove(r.binder);
- }
- }
if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -1216,10 +1203,10 @@
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
switch (activationType) {
- case TelephonyManager.SIM_ACTIVATION_TYPE_VOICE:
+ case SIM_ACTIVATION_TYPE_VOICE:
mVoiceActivationState[phoneId] = activationState;
break;
- case TelephonyManager.SIM_ACTIVATION_TYPE_DATA:
+ case SIM_ACTIVATION_TYPE_DATA:
mDataActivationState[phoneId] = activationState;
break;
default:
@@ -1232,10 +1219,10 @@
+ " state=" + activationState);
}
try {
- if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_VOICE) &&
- r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) &&
- idMatch(r.subId, subId, phoneId)) {
+ if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
+ && r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE)
+ && idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1243,10 +1230,10 @@
}
r.callback.onVoiceActivationStateChanged(activationState);
}
- if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_DATA) &&
- r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) &&
- idMatch(r.subId, subId, phoneId)) {
+ if ((activationType == SIM_ACTIVATION_TYPE_DATA)
+ && r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE)
+ && idMatch(r.subId, subId, phoneId)) {
if (DBG) {
log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1394,43 +1381,6 @@
}
}
- /**
- * Notify physical channel configuration according to subscripton ID and phone ID
- */
- public void notifyPhysicalChannelConfigurationForSubscriber(int phoneId, int subId,
- List<PhysicalChannelConfig> configs) {
- if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
- return;
- }
-
- if (VDBG) {
- log("notifyPhysicalChannelConfiguration: subId=" + subId + " phoneId=" + phoneId
- + " configs=" + configs);
- }
-
- synchronized (mRecords) {
- if (validatePhoneId(phoneId)) {
- mPhysicalChannelConfigs.set(phoneId, configs);
- for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
- && idMatch(r.subId, subId, phoneId)) {
- try {
- if (DBG_LOC) {
- log("notifyPhysicalChannelConfiguration: mPhysicalChannelConfigs="
- + configs + " r=" + r);
- }
- r.callback.onPhysicalChannelConfigurationChanged(configs);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) {
if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53a5fc6..663e8a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8365,6 +8365,31 @@
requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE);
}
+ /**
+ * Launches a bugreport-whitelisted app to handle a bugreport.
+ *
+ * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
+ * be predefined in the config, meant to be launched with the primary user. The user can
+ * override this with a different (or same) handler app on possibly a different user. This is
+ * useful for capturing bug reports from work profile, for instance.
+ *
+ * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false
+ * otherwise.
+ */
+ @Override
+ public boolean launchBugReportHandlerApp() {
+ if (!BugReportHandlerUtil.isBugReportHandlerEnabled(mContext)) {
+ return false;
+ }
+
+ // Always log caller, even if it does not have permission to dump.
+ Slog.i(TAG, "launchBugReportHandlerApp requested by UID " + Binder.getCallingUid());
+ enforceCallingPermission(android.Manifest.permission.DUMP,
+ "launchBugReportHandlerApp");
+
+ return BugReportHandlerUtil.launchBugReportHandlerApp(mContext);
+ }
+
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 908ec6b..7f1d5a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3065,7 +3065,7 @@
pw.println(" even if in the background.");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
- pw.println(" [--no-hidden-api-checks [--no-test-api-checks]]");
+ pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
pw.println(" [--no-isolated-storage]");
pw.println(" [--no-window-animation] [--abi <ABI>] <COMPONENT>");
pw.println(" Start an Instrumentation. Typically this target <COMPONENT> is in the");
@@ -3085,7 +3085,7 @@
pw.println(" --user <USER_ID> | current: Specify user instrumentation runs in;");
pw.println(" current user if not specified.");
pw.println(" --no-hidden-api-checks: disable restrictions on use of hidden API.");
- pw.println(" --no-test-api-checks: disable restrictions to test APIs, if hidden");
+ pw.println(" --no-test-api-access: do not allow access to test APIs, if hidden");
pw.println(" API checks are enabled.");
pw.println(" --no-isolated-storage: don't use isolated storage sandbox and ");
pw.println(" mount full external storage");
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 638111e..38030c2 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,22 +15,20 @@
*/
package com.android.server.am;
-import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
-
import android.annotation.Nullable;
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiManager;
import android.os.BatteryStats;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SynchronousResultReceiver;
import android.os.SystemClock;
import android.os.ThreadLocalWorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.IntArray;
@@ -45,6 +43,7 @@
import libcore.util.EmptyArray;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
@@ -117,7 +116,7 @@
private final Object mWorkerLock = new Object();
@GuardedBy("mWorkerLock")
- private IWifiManager mWifiManager = null;
+ private WifiManager mWifiManager = null;
@GuardedBy("mWorkerLock")
private TelephonyManager mTelephony = null;
@@ -411,21 +410,33 @@
if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
// We were asked to fetch WiFi data.
- if (mWifiManager == null) {
- mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
- Context.WIFI_SERVICE));
+ if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) {
+ // this code is reached very early in the boot process, before Wifi Service has
+ // been registered. Check that ServiceManager.getService() returns a non null
+ // value before calling mContext.getSystemService(), since otherwise
+ // getSystemService() will throw a ServiceNotFoundException.
+ mWifiManager = mContext.getSystemService(WifiManager.class);
}
- if (mWifiManager != null) {
- try {
- // Only fetch WiFi power data if it is supported.
- if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
- wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
- }
- } catch (RemoteException e) {
- // Oh well.
- }
+ // Only fetch WiFi power data if it is supported.
+ if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) {
+ SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.getWifiActivityEnergyInfoAsync(
+ new Executor() {
+ @Override
+ public void execute(Runnable runnable) {
+ // run the listener on the binder thread, if it was run on the main
+ // thread it would deadlock since we would be waiting on ourselves
+ runnable.run();
+ }
+ },
+ info -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ tempWifiReceiver.send(0, bundle);
+ }
+ );
+ wifiReceiver = tempWifiReceiver;
}
synchronized (mStats) {
mStats.updateRailStatsLocked();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8c60d0c..37026fd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.net.wifi.WifiActivityEnergyInfo;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
import android.os.Binder;
@@ -43,6 +42,7 @@
import android.os.WorkSource;
import android.os.connectivity.CellularBatteryStats;
import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.os.health.HealthStatsParceler;
import android.os.health.HealthStatsWriter;
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
new file mode 100644
index 0000000..ba89fce
--- /dev/null
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -0,0 +1,179 @@
+/*
+ * 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.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.BroadcastOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.SystemConfig;
+
+import java.util.List;
+
+/**
+ * Static utility methods related to BugReportHandler.
+ */
+public final class BugReportHandlerUtil {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "BugReportHandlerUtil" : TAG_AM;
+ private static final String SHELL_APP_PACKAGE = "com.android.shell";
+ private static final String INTENT_BUGREPORT_REQUESTED =
+ "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+
+ /**
+ * Check is BugReportHandler enabled on the device.
+ *
+ * @param context Context
+ * @return true if BugReportHandler is enabled, or false otherwise
+ */
+ static boolean isBugReportHandlerEnabled(Context context) {
+ return context.getResources().getBoolean(
+ com.android.internal.R.bool.config_bugReportHandlerEnabled);
+ }
+
+ /**
+ * Launches a bugreport-whitelisted app to handle a bugreport.
+ *
+ * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
+ * be predefined in the config, meant to be launched with the primary user. The user can
+ * override this with a different (or same) handler app on possibly a different user. This is
+ * useful for capturing bug reports from work profile, for instance.
+ *
+ * @param context Context
+ * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false
+ * otherwise
+ */
+ static boolean launchBugReportHandlerApp(Context context) {
+ if (!isBugReportHandlerEnabled(context)) {
+ return false;
+ }
+
+ String handlerApp = getCustomBugReportHandlerApp(context);
+ if (isShellApp(handlerApp)) {
+ return false;
+ }
+
+ int handlerUser = getCustomBugReportHandlerUser(context);
+ if (!isValidBugReportHandlerApp(handlerApp)) {
+ handlerApp = getDefaultBugReportHandlerApp(context);
+ handlerUser = UserHandle.USER_SYSTEM;
+ } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
+ // It looks like the settings are outdated, reset outdated settings.
+ //
+ // i.e.
+ // If user chooses which profile and which bugreport-whitelisted app in that
+ // profile to handle a bugreport, then user remove the profile.
+ // === RESULT ===
+ // The chosen bugreport handler app is outdated because the profile is removed,
+ // so reset the chosen app and profile
+ handlerApp = getDefaultBugReportHandlerApp(context);
+ handlerUser = UserHandle.USER_SYSTEM;
+ resetCustomBugreportHandlerAppAndUser(context);
+ }
+
+ if (isShellApp(handlerApp) || !isValidBugReportHandlerApp(handlerApp)
+ || getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
+ return false;
+ }
+
+ Slog.i(TAG, "Launching bug report handler app: " + handlerApp);
+ Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
+ intent.setPackage(handlerApp);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ // Send broadcast to the receiver while allowing starting activity from background
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setBackgroundActivityStartsAllowed(true);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
+ android.Manifest.permission.DUMP,
+ options.toBundle());
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return true;
+ }
+
+ private static String getCustomBugReportHandlerApp(Context context) {
+ // Get the package of custom bugreport handler app
+ return Settings.Global.getString(context.getContentResolver(),
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP);
+ }
+
+ private static int getCustomBugReportHandlerUser(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_NULL);
+ }
+
+ private static boolean isShellApp(String app) {
+ return SHELL_APP_PACKAGE.equals(app);
+ }
+
+ private static boolean isValidBugReportHandlerApp(String app) {
+ return !TextUtils.isEmpty(app) && isBugreportWhitelistedApp(app);
+ }
+
+ private static boolean isBugreportWhitelistedApp(String app) {
+ // Verify the app is bugreport-whitelisted
+ final ArraySet<String> whitelistedApps = SystemConfig.getInstance()
+ .getBugreportWhitelistedPackages();
+ return whitelistedApps.contains(app);
+ }
+
+ private static List<ResolveInfo> getBugReportHandlerAppReceivers(Context context,
+ String handlerApp, int handlerUser) {
+ // Use the app package and the user id to retrieve the receiver that can handle a
+ // broadcast of the intent.
+ Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
+ intent.setPackage(handlerApp);
+ return context.getPackageManager()
+ .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY,
+ handlerUser);
+ }
+
+ private static String getDefaultBugReportHandlerApp(Context context) {
+ return context.getResources().getString(
+ com.android.internal.R.string.config_defaultBugReportHandlerApp);
+ }
+
+ private static void resetCustomBugreportHandlerAppAndUser(Context context) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Global.putString(context.getContentResolver(),
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
+ getDefaultBugReportHandlerApp(context));
+ Settings.Global.putInt(context.getContentResolver(),
+ Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_SYSTEM);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0591704..12f4656 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -376,7 +376,11 @@
ConnectionRecord cr = pr.connections.valueAt(i);
ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
? cr.binding.service.isolatedProc : cr.binding.service.app;
- if (service == null || service == pr || (containsCycle |= service.mReachable)) {
+ if (service == null || service == pr) {
+ continue;
+ }
+ containsCycle |= service.mReachable;
+ if (service.mReachable) {
continue;
}
if ((cr.flags & (Context.BIND_WAIVE_PRIORITY
@@ -394,6 +398,10 @@
if (provider == null || provider == pr || (containsCycle |= provider.mReachable)) {
continue;
}
+ containsCycle |= provider.mReachable;
+ if (provider.mReachable) {
+ continue;
+ }
queue.offer(provider);
provider.mReachable = true;
}
@@ -482,12 +490,15 @@
// need to reset cycle state before calling computeOomAdjLocked because of service conns
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
- app.containsCycle = false;
app.mReachable = false;
- app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
- app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
- app.setCapability = PROCESS_CAPABILITY_NONE;
- app.resetCachedInfo();
+ // No need to compute again it has been evaluated in previous iteration
+ if (app.adjSeq != mAdjSeq) {
+ app.containsCycle = false;
+ app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+ app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+ app.setCapability = PROCESS_CAPABILITY_NONE;
+ app.resetCachedInfo();
+ }
}
for (int i = numProc - 1; i >= 0; i--) {
ProcessRecord app = activeProcesses.get(i);
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 95582f7..2eec419 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -55,7 +55,7 @@
private Map<String, Boolean> mPackageOverrides;
public CompatChange(long changeId) {
- this(changeId, null, -1, false);
+ this(changeId, null, -1, false, null);
}
/**
@@ -66,8 +66,8 @@
* @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
*/
public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
- boolean disabled) {
- super(changeId, name, enableAfterTargetSdk, disabled);
+ boolean disabled, String description) {
+ super(changeId, name, enableAfterTargetSdk, disabled, description);
}
/**
@@ -75,7 +75,7 @@
*/
public CompatChange(Change change) {
super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
- change.getDisabled());
+ change.getDisabled(), change.getDescription());
}
void registerListener(ChangeListener listener) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 39c6e75..cf83dd6 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -319,7 +319,8 @@
changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
change.getName(),
change.getEnableAfterTargetSdk(),
- change.getDisabled());
+ change.getDisabled(),
+ change.getDescription());
}
return changeInfos;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index bc83780..2179518 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -31,7 +31,6 @@
import android.net.StringNetworkSpecifier;
import android.net.wifi.WifiInfo;
import android.os.UserHandle;
-import android.telephony.AccessNetworkConstants.TransportType;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -105,8 +104,7 @@
return -1;
}
- // TODO: Remove @TransportType or change it to @Transport.
- private static String getTransportName(@TransportType int transportType) {
+ private static String getTransportName(final int transportType) {
Resources r = Resources.getSystem();
String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
try {
diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java
new file mode 100644
index 0000000..09bc7e8
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/BitInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * 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.integrity.model;
+
+/** A wrapper class for reading a stream of bits. */
+public class BitInputStream {
+
+ private byte[] mRuleBytes;
+ private long mBitPointer;
+
+ public BitInputStream(byte[] ruleBytes) {
+ this.mRuleBytes = ruleBytes;
+ this.mBitPointer = 0;
+ }
+
+ /**
+ * Read the next number of bits from the stream.
+ *
+ * @param numOfBits The number of bits to read.
+ * @return The value read from the stream.
+ */
+ public int getNext(int numOfBits) {
+ int component = 0;
+ int count = 0;
+
+ int idx = (int) (mBitPointer / 8);
+ int offset = 7 - (int) (mBitPointer % 8);
+
+ while (count++ < numOfBits) {
+ if (idx >= mRuleBytes.length) {
+ throw new IllegalArgumentException(String.format("Invalid byte index: %d", idx));
+ }
+
+ component <<= 1;
+ component |= (mRuleBytes[idx] >>> offset) & 1;
+
+ offset--;
+ if (offset == -1) {
+ idx++;
+ offset = 7;
+ }
+ }
+
+ mBitPointer += numOfBits;
+ return component;
+ }
+
+ /** Check if there are bits left in the stream. */
+ public boolean hasNext() {
+ return mBitPointer / 8 < mRuleBytes.length;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
new file mode 100644
index 0000000..ecb9189
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.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.integrity.model;
+
+import java.util.BitSet;
+
+/** A wrapper class for writing a stream of bits. */
+public class BitOutputStream {
+
+ private BitSet mBitSet;
+ private int mIndex;
+
+ public BitOutputStream() {
+ mBitSet = new BitSet();
+ mIndex = 0;
+ }
+
+ /**
+ * Set the next number of bits in the stream to value.
+ *
+ * @param numOfBits The number of bits used to represent the value.
+ * @param value The value to convert to bits.
+ */
+ public void setNext(int numOfBits, int value) {
+ if (numOfBits <= 0) {
+ return;
+ }
+ int offset = 1 << (numOfBits - 1);
+ while (numOfBits-- > 0) {
+ mBitSet.set(mIndex, (value & offset) != 0);
+ offset >>= 1;
+ mIndex++;
+ }
+ }
+
+ /**
+ * Set the next bit in the stream to value.
+ *
+ * @param value The value to set the bit to.
+ */
+ public void setNext(boolean value) {
+ mBitSet.set(mIndex, value);
+ mIndex++;
+ }
+
+ /** Set the next bit in the stream to true. */
+ public void setNext() {
+ setNext(/* value= */ true);
+ }
+
+ /** Convert BitSet in big-endian to ByteArray in big-endian. */
+ public byte[] toByteArray() {
+ int bitSetSize = mBitSet.length();
+ int numOfBytes = bitSetSize / 8;
+ if (bitSetSize % 8 != 0) {
+ numOfBytes++;
+ }
+ byte[] bytes = new byte[numOfBytes];
+ for (int i = 0; i < mBitSet.length(); i++) {
+ if (mBitSet.get(i)) {
+ bytes[i / 8] |= 1 << (7 - (i % 8));
+ }
+ }
+ return bytes;
+ }
+
+ /** Clear the stream. */
+ public void clear() {
+ mBitSet.clear();
+ mIndex = 0;
+ }
+}
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
new file mode 100644
index 0000000..d47ce2d
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
@@ -0,0 +1,41 @@
+/*
+ * 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.integrity.model;
+
+import android.content.integrity.Rule;
+
+/**
+ * A helper class containing information about the binary representation of different {@link Rule}
+ * components.
+ */
+public final class ComponentBitSize {
+ public static final int FORMAT_VERSION_BITS = 5;
+ public static final int EFFECT_BITS = 3;
+ public static final int KEY_BITS = 4;
+ public static final int OPERATOR_BITS = 3;
+ public static final int CONNECTOR_BITS = 2;
+ public static final int SEPARATOR_BITS = 2;
+ public static final int VALUE_SIZE_BITS = 5;
+ public static final int IS_HASHED_BITS = 1;
+
+ public static final int ATOMIC_FORMULA_START = 0;
+ public static final int COMPOUND_FORMULA_START = 1;
+ public static final int COMPOUND_FORMULA_END = 2;
+
+ public static final int DEFAULT_FORMAT_VERSION = 1;
+ public static final int SIGNAL_BIT = 1;
+}
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 aad177e..8aa0751 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -16,23 +16,132 @@
package com.android.server.integrity.parser;
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
import android.content.integrity.Rule;
+import com.android.server.integrity.model.BitInputStream;
+
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.List;
/** A helper class to parse rules into the {@link Rule} model from Binary representation. */
public class RuleBinaryParser implements RuleParser {
@Override
- public List<Rule> parse(byte[] ruleBytes) {
- // TODO: Implement binary text parser.
- return null;
+ public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
+ try {
+ BitInputStream bitInputStream = new BitInputStream(ruleBytes);
+ return parseRules(bitInputStream);
+ } catch (Exception e) {
+ throw new RuleParseException(e.getMessage(), e);
+ }
}
@Override
- public List<Rule> parse(InputStream inputStream) {
- // TODO: Implement stream parser.
- return null;
+ public List<Rule> parse(InputStream inputStream) throws RuleParseException {
+ try {
+ byte[] ruleBytes = new byte[inputStream.available()];
+ inputStream.read(ruleBytes);
+ return parse(ruleBytes);
+ } catch (Exception e) {
+ throw new RuleParseException(e.getMessage(), e);
+ }
+ }
+
+ private List<Rule> parseRules(BitInputStream bitInputStream) {
+ List<Rule> parsedRules = new ArrayList<>();
+
+ // Read the rule binary file format version.
+ bitInputStream.getNext(FORMAT_VERSION_BITS);
+
+ while (bitInputStream.hasNext()) {
+ if (bitInputStream.getNext(SIGNAL_BIT) == 1) {
+ parsedRules.add(parseRule(bitInputStream));
+ }
+ }
+
+ return parsedRules;
+ }
+
+ private Rule parseRule(BitInputStream bitInputStream) {
+ Formula formula = parseFormula(bitInputStream);
+ int effect = bitInputStream.getNext(EFFECT_BITS);
+
+ if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
+ throw new IllegalArgumentException("A rule must end with a '1' bit.");
+ }
+
+ return new Rule(formula, effect);
+ }
+
+ private Formula parseFormula(BitInputStream bitInputStream) {
+ int separator = bitInputStream.getNext(SEPARATOR_BITS);
+ switch (separator) {
+ case ATOMIC_FORMULA_START:
+ return parseAtomicFormula(bitInputStream);
+ case COMPOUND_FORMULA_START:
+ return parseCompoundFormula(bitInputStream);
+ case COMPOUND_FORMULA_END:
+ return null;
+ default:
+ throw new IllegalArgumentException(
+ String.format("Unknown formula separator: %s", separator));
+ }
+ }
+
+ private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) {
+ int connector = bitInputStream.getNext(CONNECTOR_BITS);
+ List<Formula> formulas = new ArrayList<>();
+
+ Formula parsedFormula = parseFormula(bitInputStream);
+ while (parsedFormula != null) {
+ formulas.add(parsedFormula);
+ parsedFormula = parseFormula(bitInputStream);
+ }
+
+ return new CompoundFormula(connector, formulas);
+ }
+
+ private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) {
+ int key = bitInputStream.getNext(KEY_BITS);
+ int operator = bitInputStream.getNext(OPERATOR_BITS);
+
+ boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
+ int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
+ StringBuilder value = new StringBuilder();
+ while (valueSize-- > 0) {
+ value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
+ }
+
+ switch (key) {
+ case AtomicFormula.PACKAGE_NAME:
+ case AtomicFormula.APP_CERTIFICATE:
+ case AtomicFormula.INSTALLER_NAME:
+ case AtomicFormula.INSTALLER_CERTIFICATE:
+ return new AtomicFormula.StringAtomicFormula(key, value.toString(), isHashedValue);
+ case AtomicFormula.VERSION_CODE:
+ return new AtomicFormula.IntAtomicFormula(
+ key, operator, Integer.parseInt(value.toString()));
+ case AtomicFormula.PRE_INSTALLED:
+ return new AtomicFormula.BooleanAtomicFormula(key, value.toString().equals("1"));
+ default:
+ throw new IllegalArgumentException(String.format("Unknown key: %d", key));
+ }
}
}
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 2e99d0f..d405583 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -41,7 +41,7 @@
private static final String NAMESPACE = "";
private static final String RULE_LIST_TAG = "RL";
private static final String RULE_TAG = "R";
- private static final String OPEN_FORMULA_TAG = "OF";
+ private static final String COMPOUND_FORMULA_TAG = "OF";
private static final String ATOMIC_FORMULA_TAG = "AF";
private static final String EFFECT_ATTRIBUTE = "E";
private static final String KEY_ATTRIBUTE = "K";
@@ -118,8 +118,8 @@
if (eventType == XmlPullParser.START_TAG) {
switch (nodeName) {
- case OPEN_FORMULA_TAG:
- formula = parseOpenFormula(parser);
+ case COMPOUND_FORMULA_TAG:
+ formula = parseCompoundFormula(parser);
break;
case ATOMIC_FORMULA_TAG:
formula = parseAtomicFormula(parser);
@@ -137,7 +137,7 @@
return new Rule(formula, effect);
}
- private static Formula parseOpenFormula(XmlPullParser parser)
+ private static Formula parseCompoundFormula(XmlPullParser parser)
throws IOException, XmlPullParserException {
int connector =
Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1"));
@@ -147,7 +147,8 @@
while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
String nodeName = parser.getName();
- if (eventType == XmlPullParser.END_TAG && parser.getName().equals(OPEN_FORMULA_TAG)) {
+ if (eventType == XmlPullParser.END_TAG
+ && parser.getName().equals(COMPOUND_FORMULA_TAG)) {
break;
}
@@ -156,8 +157,8 @@
case ATOMIC_FORMULA_TAG:
formulas.add(parseAtomicFormula(parser));
break;
- case OPEN_FORMULA_TAG:
- formulas.add(parseOpenFormula(parser));
+ case COMPOUND_FORMULA_TAG:
+ formulas.add(parseCompoundFormula(parser));
break;
default:
throw new RuntimeException(
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 d4f41eb..b988fd4 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -16,24 +16,155 @@
package com.android.server.integrity.serializer;
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
import android.content.integrity.Rule;
+import com.android.server.integrity.model.BitOutputStream;
+
+import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
+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. */
+/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
public class RuleBinarySerializer implements RuleSerializer {
+ // Get the byte representation for a list of rules, and write them to an output stream.
@Override
public void serialize(
- List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream) {
- // TODO: Implement stream serializer.
+ List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+ throws RuleSerializeException {
+ try {
+ BitOutputStream bitOutputStream = new BitOutputStream();
+
+ int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
+ bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
+ outputStream.write(bitOutputStream.toByteArray());
+
+ for (Rule rule : rules) {
+ bitOutputStream.clear();
+ serializeRule(rule, bitOutputStream);
+ outputStream.write(bitOutputStream.toByteArray());
+ }
+ } catch (Exception e) {
+ throw new RuleSerializeException(e.getMessage(), e);
+ }
}
+ // Get the byte representation for a list of rules.
@Override
- public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion) {
- // TODO: Implement text serializer.
- return null;
+ public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
+ throws RuleSerializeException {
+ try {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ serialize(rules, formatVersion, byteArrayOutputStream);
+ return byteArrayOutputStream.toByteArray();
+ } catch (Exception e) {
+ throw new RuleSerializeException(e.getMessage(), e);
+ }
+ }
+
+ private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
+ if (rule == null) {
+ throw new IllegalArgumentException("Null rule can not be serialized");
+ }
+
+ // Start with a '1' bit to mark the start of a rule.
+ bitOutputStream.setNext();
+
+ serializeFormula(rule.getFormula(), bitOutputStream);
+ bitOutputStream.setNext(EFFECT_BITS, rule.getEffect());
+
+ // End with a '1' bit to mark the end of a rule.
+ bitOutputStream.setNext();
+ }
+
+ private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) {
+ if (formula instanceof AtomicFormula) {
+ serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
+ } else if (formula instanceof CompoundFormula) {
+ serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Invalid formula type: %s", formula.getClass()));
+ }
+ }
+
+ private void serializeCompoundFormula(
+ CompoundFormula compoundFormula, BitOutputStream bitOutputStream) {
+ if (compoundFormula == null) {
+ throw new IllegalArgumentException("Null compound formula can not be serialized");
+ }
+
+ bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
+ bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
+ for (Formula formula : compoundFormula.getFormulas()) {
+ serializeFormula(formula, bitOutputStream);
+ }
+ bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
+ }
+
+ private void serializeAtomicFormula(
+ AtomicFormula atomicFormula, BitOutputStream bitOutputStream) {
+ if (atomicFormula == null) {
+ throw new IllegalArgumentException("Null atomic formula can not be serialized");
+ }
+
+ bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START);
+ bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey());
+ if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) {
+ AtomicFormula.StringAtomicFormula stringAtomicFormula =
+ (AtomicFormula.StringAtomicFormula) atomicFormula;
+ bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
+ serializeValue(
+ stringAtomicFormula.getValue(),
+ stringAtomicFormula.getIsHashedValue(),
+ bitOutputStream);
+ } else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) {
+ AtomicFormula.IntAtomicFormula intAtomicFormula =
+ (AtomicFormula.IntAtomicFormula) atomicFormula;
+ bitOutputStream.setNext(OPERATOR_BITS, intAtomicFormula.getOperator());
+ serializeValue(
+ String.valueOf(intAtomicFormula.getValue()),
+ /* isHashedValue= */ false,
+ bitOutputStream);
+ } else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) {
+ AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
+ (AtomicFormula.BooleanAtomicFormula) atomicFormula;
+ bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
+ serializeValue(
+ booleanAtomicFormula.getValue() ? "1" : "0",
+ /* isHashedValue= */ false,
+ bitOutputStream);
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
+ }
+ }
+
+ private void serializeValue(
+ String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
+ byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
+
+ bitOutputStream.setNext(isHashedValue);
+ bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length);
+ for (byte valueByte : valueBytes) {
+ bitOutputStream.setNext(/* numOfBits= */ 8, valueByte);
+ }
}
}
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 3ec9cf2..72068ce 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -39,7 +39,7 @@
private static final String RULE_LIST_TAG = "RL";
private static final String RULE_TAG = "R";
- private static final String OPEN_FORMULA_TAG = "OF";
+ private static final String COMPOUND_FORMULA_TAG = "OF";
private static final String ATOMIC_FORMULA_TAG = "AF";
private static final String EFFECT_ATTRIBUTE = "E";
private static final String KEY_ATTRIBUTE = "K";
@@ -78,13 +78,13 @@
private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer) throws IOException {
xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG);
for (Rule rule : rules) {
- serialize(rule, xmlSerializer);
+ serializeRule(rule, xmlSerializer);
}
xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG);
xmlSerializer.endDocument();
}
- private void serialize(Rule rule, XmlSerializer xmlSerializer) throws IOException {
+ private void serializeRule(Rule rule, XmlSerializer xmlSerializer) throws IOException {
if (rule == null) {
return;
}
@@ -98,25 +98,25 @@
if (formula instanceof AtomicFormula) {
serializeAtomicFormula((AtomicFormula) formula, xmlSerializer);
} else if (formula instanceof CompoundFormula) {
- serializeOpenFormula((CompoundFormula) formula, xmlSerializer);
+ serializeCompoundFormula((CompoundFormula) formula, xmlSerializer);
} else {
throw new IllegalArgumentException(
String.format("Invalid formula type: %s", formula.getClass()));
}
}
- private void serializeOpenFormula(CompoundFormula compoundFormula, XmlSerializer xmlSerializer)
- throws IOException {
+ private void serializeCompoundFormula(
+ CompoundFormula compoundFormula, XmlSerializer xmlSerializer) throws IOException {
if (compoundFormula == null) {
return;
}
- xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG);
+ xmlSerializer.startTag(NAMESPACE, COMPOUND_FORMULA_TAG);
serializeAttributeValue(
CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
for (Formula formula : compoundFormula.getFormulas()) {
serializeFormula(formula, xmlSerializer);
}
- xmlSerializer.endTag(NAMESPACE, OPEN_FORMULA_TAG);
+ xmlSerializer.endTag(NAMESPACE, COMPOUND_FORMULA_TAG);
}
private void serializeAtomicFormula(AtomicFormula atomicFormula, XmlSerializer xmlSerializer)
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 675e59e..45d9bae 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -29,10 +29,12 @@
import android.hardware.location.IContextHubClient;
import android.hardware.location.IContextHubClientCallback;
import android.hardware.location.NanoAppMessage;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
/**
@@ -97,6 +99,16 @@
private final PendingIntentRequest mPendingIntentRequest;
/*
+ * The host package associated with this client.
+ */
+ private final String mPackage;
+
+ /*
+ * True if a PendingIntent has been cancelled.
+ */
+ private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
+
+ /*
* Helper class to manage registered PendingIntent requests from the client.
*/
private class PendingIntentRequest {
@@ -110,11 +122,14 @@
*/
private long mNanoAppId;
+ private boolean mValid = false;
+
PendingIntentRequest() {}
PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) {
mPendingIntent = pendingIntent;
mNanoAppId = nanoAppId;
+ mValid = true;
}
public long getNanoAppId() {
@@ -132,6 +147,10 @@
public void clear() {
mPendingIntent = null;
}
+
+ public boolean isValid() {
+ return mValid;
+ }
}
/* package */ ContextHubClientBroker(
@@ -145,6 +164,7 @@
mHostEndPointId = hostEndPointId;
mCallbackInterface = callback;
mPendingIntentRequest = new PendingIntentRequest();
+ mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
/* package */ ContextHubClientBroker(
@@ -157,6 +177,7 @@
mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
+ mPackage = pendingIntent.getCreatorPackage();
}
/**
@@ -313,6 +334,13 @@
}
/**
+ * @return true if the client is a PendingIntent client that has been cancelled.
+ */
+ /* package */ boolean isPendingIntentCancelled() {
+ return mIsPendingIntentCancelled.get();
+ }
+
+ /**
* Helper function to invoke a specified client callback, if the connection is open.
*
* @param consumer the consumer specifying the callback to invoke
@@ -392,6 +420,7 @@
Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
null /* options */);
} catch (PendingIntent.CanceledException e) {
+ mIsPendingIntentCancelled.set(true);
// The PendingIntent is no longer valid
Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
+ " (host endpoint ID " + mHostEndPointId + ")");
@@ -419,4 +448,20 @@
mRegistered = false;
}
}
+
+ @Override
+ public String toString() {
+ String out = "[ContextHubClient ";
+ out += "endpointID: " + getHostEndPointId() + ", ";
+ out += "contextHub: " + getAttachedContextHubId() + ", ";
+ if (mPendingIntentRequest.isValid()) {
+ out += "intentCreatorPackage: " + mPackage + ", ";
+ out += "nanoAppId: 0x" + Long.toHexString(mPendingIntentRequest.getNanoAppId());
+ } else {
+ out += "package: " + mPackage;
+ }
+ out += "]";
+
+ return out;
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 00b7d62..46db8dc 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.IntDef;
import android.app.PendingIntent;
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -27,7 +28,12 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Calendar;
+import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Consumer;
/**
@@ -71,6 +77,79 @@
*/
private int mNextHostEndPointId = 0;
+ /*
+ * The list of previous registration records.
+ */
+ private static final int NUM_CLIENT_RECORDS = 20;
+ private final ConcurrentLinkedEvictingDeque<RegistrationRecord> mRegistrationRecordDeque =
+ new ConcurrentLinkedEvictingDeque<>(NUM_CLIENT_RECORDS);
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ACTION_" }, value = {
+ ACTION_REGISTERED,
+ ACTION_UNREGISTERED,
+ ACTION_CANCELLED,
+ })
+ public @interface Action {}
+ public static final int ACTION_REGISTERED = 0;
+ public static final int ACTION_UNREGISTERED = 1;
+ public static final int ACTION_CANCELLED = 2;
+
+ /**
+ * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
+ */
+ private class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
+ private int mSize;
+
+ ConcurrentLinkedEvictingDeque(int size) {
+ mSize = size;
+ }
+
+ @Override
+ public boolean add(E elem) {
+ synchronized (this) {
+ if (size() == mSize) {
+ poll();
+ }
+
+ return super.add(elem);
+ }
+ }
+ }
+
+ /**
+ * A container class to store a record of ContextHubClient registration.
+ */
+ private class RegistrationRecord {
+ private final String mBroker;
+ private final int mAction;
+ private final String mDate;
+
+ RegistrationRecord(String broker, @Action int action) {
+ mBroker = broker;
+ mAction = action;
+ Calendar instance = Calendar.getInstance();
+ mDate = String.format("%02d", instance.get(Calendar.MONTH) + 1) // Jan == 0
+ + "/" + String.format("%02d", instance.get(Calendar.DAY_OF_MONTH))
+ + " " + String.format("%02d", instance.get(Calendar.HOUR_OF_DAY))
+ + ":" + String.format("%02d", instance.get(Calendar.MINUTE))
+ + ":" + String.format("%02d", instance.get(Calendar.SECOND))
+ + "." + String.format("%03d", instance.get(Calendar.MILLISECOND));
+ }
+
+ @Override
+ public String toString() {
+ String out = "";
+ out += mDate + " ";
+ out += mAction == ACTION_REGISTERED ? "+ " : "- ";
+ out += mBroker;
+ if (mAction == ACTION_CANCELLED) {
+ out += " (cancelled)";
+ }
+ return out;
+ }
+ }
+
/* package */ ContextHubClientManager(
Context context, IContexthub contextHubProxy) {
mContext = context;
@@ -96,6 +175,8 @@
mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
hostEndPointId, clientCallback);
mHostEndPointIdToClientMap.put(hostEndPointId, broker);
+ mRegistrationRecordDeque.add(
+ new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
}
try {
@@ -136,6 +217,8 @@
hostEndPointId, pendingIntent, nanoAppId);
mHostEndPointIdToClientMap.put(hostEndPointId, broker);
registerString = "Registered";
+ mRegistrationRecordDeque.add(
+ new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
}
}
@@ -178,6 +261,13 @@
* @param hostEndPointId the host endpoint ID of the client that has died
*/
/* package */ void unregisterClient(short hostEndPointId) {
+ ContextHubClientBroker broker = mHostEndPointIdToClientMap.get(hostEndPointId);
+ if (broker != null) {
+ @Action int action =
+ broker.isPendingIntentCancelled() ? ACTION_CANCELLED : ACTION_UNREGISTERED;
+ mRegistrationRecordDeque.add(new RegistrationRecord(broker.toString(), action));
+ }
+
if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
} else {
@@ -285,4 +375,20 @@
return null;
}
+
+ @Override
+ public String toString() {
+ String out = "";
+ for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+ out += broker + "\n";
+ }
+
+ out += "\nRegistration history:\n";
+ Iterator<RegistrationRecord> it = mRegistrationRecordDeque.descendingIterator();
+ while (it.hasNext()) {
+ out += it.next() + "\n";
+ }
+
+ return out;
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 36b0342..787a800 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -795,6 +795,10 @@
// Dump nanoAppHash
mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
+ pw.println("");
+ pw.println("=================== CLIENTS ====================");
+ pw.println(mClientManager);
+
// dump eventLog
}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ec11a97..14ef2d3a 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -136,8 +136,11 @@
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
properties -> {
- synchronized (FeatureConfigImpl.this) {
- mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, false);
+ if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) {
+ synchronized (FeatureConfigImpl.this) {
+ mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME,
+ false);
+ }
}
});
}
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
new file mode 100644
index 0000000..0719797
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -0,0 +1,246 @@
+/*
+ * 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 android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderManager;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.SystemService;
+
+import java.util.List;
+
+/**
+ * Data loader manager service manages data loader binder services.
+ *
+ * @hide
+ */
+public class DataLoaderManagerService extends SystemService {
+ private static final String TAG = "DataLoaderManager";
+ private final Context mContext;
+ private final DataLoaderManagerBinderService mBinderService;
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private SparseArray<DataLoaderServiceConnection> mServiceConnections;
+
+ public DataLoaderManagerService(Context context) {
+ super(context);
+ mContext = context;
+ mBinderService = new DataLoaderManagerBinderService();
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.DATA_LOADER_MANAGER_SERVICE, mBinderService);
+ }
+
+ final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
+ @Override
+ public boolean initializeDataLoader(int dataLoaderId, Bundle params,
+ IDataLoaderStatusListener listener) {
+ synchronized (mLock) {
+ if (mServiceConnections == null) {
+ mServiceConnections = new SparseArray<>();
+ }
+ if (mServiceConnections.get(dataLoaderId) != null) {
+ Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists.");
+ return false;
+ }
+ }
+ CharSequence packageNameSeq = params.getCharSequence("packageName");
+ if (packageNameSeq == null) {
+ Slog.e(TAG, "Must specify package name.");
+ return false;
+ }
+ String packageName = packageNameSeq.toString();
+ ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName);
+ if (dataLoaderComponent == null) {
+ return false;
+ }
+ // Binds to the specific data loader service
+ DataLoaderServiceConnection connection =
+ new DataLoaderServiceConnection(dataLoaderId, params, listener);
+ Intent intent = new Intent();
+ intent.setComponent(dataLoaderComponent);
+ if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+ UserHandle.of(UserHandle.getCallingUserId()))) {
+ Slog.e(TAG, "Failed to bind to data loader binder service.");
+ mContext.unbindService(connection);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Find the ComponentName of the data loader service provider, given its package name.
+ *
+ * @param packageName the package name of the provider.
+ * @return ComponentName of the data loader service provider. Null if provider not found.
+ */
+ private @Nullable ComponentName getDataLoaderServiceName(String packageName) {
+ final PackageManager pm = mContext.getPackageManager();
+ if (pm == null) {
+ Slog.e(TAG, "PackageManager is not available.");
+ return null;
+ }
+ Intent intent = new Intent(Intent.ACTION_LOAD_DATA);
+ intent.setPackage(packageName);
+ List<ResolveInfo> services =
+ pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId());
+ if (services == null || services.isEmpty()) {
+ Slog.e(TAG,
+ "Failed to find data loader service provider in package " + packageName);
+ return null;
+ }
+
+ // TODO(b/136132412): better way to enable privileged data loaders in tests
+ boolean checkLoader =
+ android.os.SystemProperties.getBoolean("incremental.check_loader", false);
+ int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ ResolveInfo ri = services.get(i);
+ ComponentName componentName = new ComponentName(
+ ri.serviceInfo.packageName, ri.serviceInfo.name);
+ // There should only be one matching provider inside the given package.
+ // If there's more than one, return the first one found.
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0);
+ if (checkLoader && !ai.isPrivilegedApp()) {
+ Slog.w(TAG,
+ "Data loader: " + componentName.getPackageName()
+ + " is not a privileged app, skipping.");
+ continue;
+ }
+ return componentName;
+ } catch (PackageManager.NameNotFoundException ex) {
+ Slog.w(TAG,
+ "Privileged data loader: " + componentName.getPackageName()
+ + " not found, skipping.");
+ }
+
+ }
+ Slog.e(TAG, "Didn't find any matching data loader service provider.");
+ return null;
+ }
+
+ /**
+ * Returns the binder object of a data loader, specified by its ID.
+ */
+ @Override
+ public @Nullable IDataLoader getDataLoader(int dataLoaderId) {
+ synchronized (mLock) {
+ if (mServiceConnections == null) {
+ return null;
+ }
+ DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
+ dataLoaderId, null);
+ if (serviceConnection == null) {
+ return null;
+ }
+ return serviceConnection.getDataLoader();
+ }
+ }
+
+ /**
+ * Destroys a data loader binder service, specified by its ID.
+ */
+ @Override
+ public void destroyDataLoader(int dataLoaderId) {
+ synchronized (mLock) {
+ if (mServiceConnections == null) {
+ return;
+ }
+ DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
+ dataLoaderId, null);
+
+ if (serviceConnection == null) {
+ return;
+ }
+ serviceConnection.destroy();
+ }
+ }
+ }
+
+ class DataLoaderServiceConnection implements ServiceConnection {
+ final int mId;
+ final Bundle mParams;
+ final IDataLoaderStatusListener mListener;
+ IDataLoader mDataLoader;
+
+ DataLoaderServiceConnection(int id, Bundle params, IDataLoaderStatusListener listener) {
+ mId = id;
+ mParams = params;
+ mListener = listener;
+ mDataLoader = null;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mDataLoader = IDataLoader.Stub.asInterface(service);
+ synchronized (mLock) {
+ mServiceConnections.append(mId, this);
+ }
+ try {
+ mDataLoader.create(mId, mParams, mListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to create data loader service.", e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName arg0) {
+ remove();
+ }
+
+ IDataLoader getDataLoader() {
+ return mDataLoader;
+ }
+
+ void destroy() {
+ try {
+ mDataLoader.destroy();
+ } catch (RemoteException ignored) {
+ }
+ mContext.unbindService(this);
+ }
+
+ private void remove() {
+ synchronized (mLock) {
+ mServiceConnections.remove(mId);
+ if (mServiceConnections.size() == 0) {
+ mServiceConnections = null;
+ }
+ }
+ mParams.clear();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4852947..883baf7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -54,6 +54,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
+import android.content.pm.InstallationFile;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
@@ -81,6 +82,8 @@
import android.os.RevocableFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.incremental.IncrementalFileStorages;
+import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.system.ErrnoException;
@@ -309,6 +312,8 @@
@GuardedBy("mLock")
private boolean mVerityFound;
+ private IncrementalFileStorages mIncrementalFileStorages;
+
private static final FileFilter sAddedFilter = new FileFilter() {
@Override
public boolean accept(File file) {
@@ -471,6 +476,18 @@
mStagedSessionErrorCode = stagedSessionErrorCode;
mStagedSessionErrorMessage =
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
+
+ // TODO(b/136132412): sanity check if session should not be incremental
+ if (!params.isStaged && params.incrementalParams != null
+ && !params.incrementalParams.getPackageName().isEmpty()) {
+ IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService(
+ Context.INCREMENTAL_SERVICE);
+ if (incrementalManager != null) {
+ mIncrementalFileStorages =
+ new IncrementalFileStorages(mPackageName, stageDir, incrementalManager,
+ params.incrementalParams);
+ }
+ }
}
public SessionInfo generateInfo() {
@@ -874,10 +891,18 @@
}
}
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.finishSetUp();
+ }
+
mHandler.obtainMessage(MSG_SEAL, statusReceiver).sendToTarget();
}
private void handleSeal(@NonNull IntentSender statusReceiver) {
+ // TODO(b/136132412): update with new APIs
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.startLoading();
+ }
if (!markAsCommitted(statusReceiver)) {
return;
}
@@ -1492,6 +1517,7 @@
computeProgressLocked(true);
// Unpack native libraries
+ // TODO(b/136132412): skip for incremental installation
extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
@@ -2182,7 +2208,6 @@
}
destroyInternal();
}
-
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
}
@@ -2268,6 +2293,20 @@
return mParentSessionId;
}
+ @Override
+ public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
+ if (mIncrementalFileStorages == null) {
+ throw new IllegalStateException(
+ "Cannot add Incremental File to a non-Incremental session.");
+ }
+ try {
+ mIncrementalFileStorages.addFile(new InstallationFile(name, size, metadata));
+ } catch (IOException ex) {
+ throw new IllegalStateException(
+ "Failed to add and configure Incremental File: " + name, ex);
+ }
+ }
+
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
final IntentSender statusReceiver;
final String packageName;
@@ -2390,6 +2429,9 @@
// since these packages are supposed to be read on reboot.
// Those dirs are deleted when the staged session has reached a final state.
if (stageDir != null && !params.isStaged) {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ }
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
@@ -2403,6 +2445,9 @@
mSessionProvider.getSession(childSessionId).cleanStageDir();
}
} else {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ }
try {
mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
} catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4e0e4ff..c5e7942 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -153,6 +153,7 @@
import android.content.pm.IPackageManagerNative;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.InstrumentationInfo;
@@ -20128,19 +20129,95 @@
public String getInstallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
synchronized (mLock) {
- final PackageSetting ps = mSettings.mPackages.get(packageName);
- if (shouldFilterApplicationLocked(
- ps, callingUid, UserHandle.getUserId(callingUid))) {
- return null;
+ final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+ if (installSource == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
}
- // InstallerPackageName for Apex is not stored in PackageManager
- if (ps == null && mApexManager.isApexPackage(packageName)) {
- return null;
+ String installerPackageName = installSource.installerPackageName;
+ if (installerPackageName != null) {
+ final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
+ UserHandle.getUserId(callingUid))) {
+ installerPackageName = null;
+ }
}
- return mSettings.getInstallerPackageNameLPr(packageName);
+ return installerPackageName;
}
}
+ @Override
+ @Nullable
+ public InstallSourceInfo getInstallSourceInfo(String packageName) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+
+ String installerPackageName;
+ String initiatingPackageName;
+ String originatingPackageName;
+
+ synchronized (mLock) {
+ final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+ if (installSource == null) {
+ return null;
+ }
+
+ installerPackageName = installSource.installerPackageName;
+ if (installerPackageName != null) {
+ final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ installerPackageName = null;
+ }
+ }
+
+ // All installSource strings are interned, so == is ok here
+ if (installSource.initiatingPackageName == installSource.installerPackageName) {
+ // The installer and initiator will often be the same, and when they are
+ // we can skip doing the same check again.
+ initiatingPackageName = installerPackageName;
+ } else {
+ initiatingPackageName = installSource.initiatingPackageName;
+ final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ initiatingPackageName = null;
+ }
+
+ }
+ originatingPackageName = installSource.originatingPackageName;
+ if (originatingPackageName != null) {
+ final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+ originatingPackageName = null;
+ }
+ }
+ }
+
+ if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
+ Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
+ originatingPackageName = null;
+ }
+
+ return new InstallSourceInfo(initiatingPackageName, originatingPackageName,
+ installerPackageName);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private InstallSource getInstallSourceLocked(String packageName, int callingUid) {
+ final PackageSetting ps = mSettings.mPackages.get(packageName);
+
+ // Installer info for Apex is not stored in PackageManager
+ if (ps == null && mApexManager.isApexPackage(packageName)) {
+ return InstallSource.EMPTY;
+ }
+
+ if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
+ UserHandle.getUserId(callingUid))) {
+ return null;
+ }
+
+ return ps.installSource;
+ }
+
public boolean isOrphaned(String packageName) {
// reader
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1b73602..b961096 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1854,22 +1854,23 @@
if (internal.isApexPackage(packageName)) {
internal.uninstallApex(
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);
- if (info == null) {
- pw.println("Failure [not installed for " + translatedUserId + "]");
- return 1;
+ } else {
+ if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
+ final PackageInfo info = mInterface.getPackageInfo(packageName,
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
+ if (info == null) {
+ pw.println("Failure [not installed for " + translatedUserId + "]");
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
}
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
-
mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
versionCode), null /*callerPackageName*/, flags,
receiver.getIntentSender(), translatedUserId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f67d3c0..6e67687 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4257,14 +4257,6 @@
return userState.isMatch(componentInfo, flags);
}
- String getInstallerPackageNameLPr(String packageName) {
- final PackageSetting pkg = mPackages.get(packageName);
- if (pkg == null) {
- throw new IllegalArgumentException("Unknown package: " + packageName);
- }
- return pkg.installSource.installerPackageName;
- }
-
boolean isOrphaned(String packageName) {
final PackageSetting pkg = mPackages.get(packageName);
if (pkg == null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 14cbe75..ad4411c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3452,8 +3452,13 @@
/**
* Find the current guest user. If the Guest user is partial,
* then do not include it in the results as it is about to die.
+ *
+ * @return The current guest user. Null if it doesn't exist.
+ * @hide
*/
- private UserInfo findCurrentGuestUser() {
+ @Override
+ public UserInfo findCurrentGuestUser() {
+ checkManageUsersPermission("findCurrentGuestUser");
synchronized (mUsersLock) {
final int size = mUsers.size();
for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3c4e3f6..9b9f93f 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,10 +29,8 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -171,23 +169,6 @@
} catch (RemoteException doesNotHappen) {
Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
}
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addDataScheme("package");
-
- getContext().registerReceiverAsUser(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- UserHandle user =
- UserHandle.getUserHandleForUid(intent.getIntExtra(Intent.EXTRA_UID, -1));
- new PermissionControllerManager(
- getUserContext(getContext(), user), FgThread.getHandler())
- .updateUserSensitive();
- }
- }, UserHandle.ALL, intentFilter, null, null);
-
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 114a16a..14617d3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -203,8 +203,11 @@
// System Property indicating that retail demo mode is currently enabled.
private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled";
- // Possible reasons for shutting down or reboot for use in REBOOT_PROPERTY(sys.boot.reason)
- // which is set by bootstat
+ // System property for last reboot reason
+ private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
+ // Possible reasons for shutting down or reboot for use in
+ // SYSTEM_PROPERTY_REBOOT_REASON(sys.boot.reason) which is set by bootstat
private static final String REASON_SHUTDOWN = "shutdown";
private static final String REASON_REBOOT = "reboot";
private static final String REASON_USERREQUESTED = "shutdown,userrequested";
@@ -225,9 +228,6 @@
private static final int HALT_MODE_REBOOT = 1;
private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
- // property for last reboot reason
- private static final String REBOOT_PROPERTY = "sys.boot.reason";
-
private final Context mContext;
private final ServiceThread mHandlerThread;
private final PowerManagerHandler mHandler;
@@ -240,6 +240,7 @@
private final BinderService mBinderService;
private final LocalService mLocalService;
private final NativeWrapper mNativeWrapper;
+ private final SystemPropertiesWrapper mSystemProperties;
private final Injector mInjector;
private LightsManager mLightsManager;
@@ -756,6 +757,20 @@
InattentiveSleepWarningController createInattentiveSleepWarningController() {
return new InattentiveSleepWarningController();
}
+
+ public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+ return new SystemPropertiesWrapper() {
+ @Override
+ public String get(String key, String def) {
+ return SystemProperties.get(key, def);
+ }
+
+ @Override
+ public void set(String key, String val) {
+ SystemProperties.set(key, val);
+ }
+ };
+ }
}
final Constants mConstants;
@@ -781,6 +796,7 @@
mBinderService = new BinderService();
mLocalService = new LocalService();
mNativeWrapper = injector.createNativeWrapper();
+ mSystemProperties = injector.createSystemPropertiesWrapper();
mInjector = injector;
mHandlerThread = new ServiceThread(TAG,
@@ -816,7 +832,7 @@
mHalInteractiveModeEnabled = true;
mWakefulness = WAKEFULNESS_AWAKE;
- sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
+ sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
mNativeWrapper.nativeInit(this);
mNativeWrapper.nativeSetAutoSuspend(false);
@@ -1067,8 +1083,9 @@
}
final String retailDemoValue = UserManager.isDeviceInDemoMode(mContext) ? "1" : "0";
- if (!retailDemoValue.equals(SystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED))) {
- SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue);
+ if (!retailDemoValue.equals(
+ mSystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, null))) {
+ mSystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue);
}
mScreenBrightnessModeSetting = Settings.System.getIntForUser(resolver,
@@ -4820,7 +4837,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- return getLastShutdownReasonInternal(REBOOT_PROPERTY);
+ return getLastShutdownReasonInternal();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -5054,12 +5071,8 @@
}
@VisibleForTesting
- // lastRebootReasonProperty argument to permit testing
- int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
- String line = SystemProperties.get(lastRebootReasonProperty);
- if (line == null) {
- return PowerManager.SHUTDOWN_REASON_UNKNOWN;
- }
+ int getLastShutdownReasonInternal() {
+ String line = mSystemProperties.get(SYSTEM_PROPERTY_REBOOT_REASON, null);
switch (line) {
case REASON_SHUTDOWN:
return PowerManager.SHUTDOWN_REASON_SHUTDOWN;
diff --git a/services/core/java/com/android/server/power/SystemPropertiesWrapper.java b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java
new file mode 100644
index 0000000..1acf798
--- /dev/null
+++ b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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.power;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Wrapper interface to access {@link SystemProperties}.
+ *
+ * @hide
+ */
+@VisibleForTesting
+interface SystemPropertiesWrapper {
+ /**
+ * Get the String value for the given {@code key}.
+ *
+ * @param key the key to lookup
+ * @param def the default value in case the property is not set or empty
+ * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
+ * string otherwise
+ */
+ @NonNull
+ String get(@NonNull String key, @Nullable String def);
+
+ /**
+ * Set the value for the given {@code key} to {@code val}.
+ *
+ * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+ * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+ * SELinux. libc will log the underlying reason.
+ */
+ void set(@NonNull String key, @Nullable String val);
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 00779ec..3f5e2a4 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -57,6 +57,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
+import com.android.server.PackageWatchdog;
import com.android.server.Watchdog;
import com.android.server.pm.Installer;
@@ -1200,6 +1201,8 @@
for (Rollback rollback : mRollbacks) {
rollback.dump(ipw);
}
+ ipw.println();
+ PackageWatchdog.getInstance(mContext).dump(ipw);
}
}
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 0b970bf..340fe3d 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -150,7 +150,8 @@
if (!mCallback.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is not enabled."
- + " time=" + time
+ + " origin=" + origin
+ + ", time=" + time
+ ", cause=" + cause);
}
return;
@@ -159,7 +160,8 @@
if (mCallback.isAutoTimeDetectionEnabled()) {
if (DBG) {
Slog.d(LOG_TAG, "Auto time detection is enabled."
- + " time=" + time
+ + " origin=" + origin
+ + ", time=" + time
+ ", cause=" + cause);
}
return;
@@ -232,16 +234,16 @@
@Override
public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
- pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
- pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
- pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
- pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+ ipw.println("TimeDetectorStrategy:");
+ ipw.increaseIndent(); // level 1
+
+ ipw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
+ ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+ ipw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
+ ipw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
+ mLastAutoSystemClockTimeSendNetworkBroadcast);
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-
- ipw.println("TimeDetectorStrategyImpl logs:");
- ipw.increaseIndent(); // level 1
ipw.println("Time change log:");
ipw.increaseIndent(); // level 2
@@ -249,6 +251,7 @@
ipw.decreaseIndent(); // level 2
ipw.decreaseIndent(); // level 1
+ ipw.flush();
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 32cee2d..0a6c2e7 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -39,6 +39,12 @@
/**
* The interface used by the strategy to interact with the surrounding service.
+ *
+ * <p>Note: Because the system properties-derived value {@link #isAutoTimeDetectionEnabled()}
+ * can be modified independently and from different threads (and processes!). its use is prone
+ * to race conditions. That will be true until the responsibility for setting their values is
+ * moved to {@link TimeDetectorStrategy}. There are similar issues with
+ * {@link #systemClockMillis()} while any process can modify the system clock.
*/
interface Callback {
diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
index 03c96e9..6bdb5ce 100644
--- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
+++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
@@ -55,7 +55,16 @@
* Default constructor using {@code system_server} tags.
*/
public TimingsTraceAndSlog() {
- this(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+ this(SYSTEM_SERVER_TIMING_TAG);
+ }
+
+ /**
+ * Custom constructor using {@code system_server} trace tag.
+ *
+ * @param tag {@code logcat} tag
+ */
+ public TimingsTraceAndSlog(@NonNull String tag) {
+ this(tag, Trace.TRACE_TAG_SYSTEM_SERVER);
}
/**
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d66aa18..03139d2e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -104,6 +104,7 @@
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.WindowManagerInternal;
import libcore.io.IoUtils;
@@ -1785,25 +1786,26 @@
@Override
public void onUnlockUser(final int userId) {
- synchronized (mLock) {
- if (mCurrentUserId == userId) {
- if (mWaitingForUnlock) {
- // the desired wallpaper is not direct-boot aware, load it now
- final WallpaperData systemWallpaper =
- getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- switchWallpaper(systemWallpaper, null);
- notifyCallbacksLocked(systemWallpaper);
- }
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("on-unlock-user-" + userId);
+ try {
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ if (mWaitingForUnlock) {
+ // the desired wallpaper is not direct-boot aware, load it now
+ final WallpaperData systemWallpaper =
+ getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ switchWallpaper(systemWallpaper, null);
+ notifyCallbacksLocked(systemWallpaper);
+ }
- // Make sure that the SELinux labeling of all the relevant files is correct.
- // This corrects for mislabeling bugs that might have arisen from move-to
- // operations involving the wallpaper files. This isn't timing-critical,
- // so we do it in the background to avoid holding up the user unlock operation.
- if (!mUserRestorecon.get(userId)) {
- mUserRestorecon.put(userId, true);
- Runnable relabeler = new Runnable() {
- @Override
- public void run() {
+ // Make sure that the SELinux labeling of all the relevant files is correct.
+ // This corrects for mislabeling bugs that might have arisen from move-to
+ // operations involving the wallpaper files. This isn't timing-critical,
+ // so we do it in the background to avoid holding up the user unlock operation.
+ if (!mUserRestorecon.get(userId)) {
+ mUserRestorecon.put(userId, true);
+ Runnable relabeler = () -> {
final File wallpaperDir = getWallpaperDir(userId);
for (String filename : sPerUserFiles) {
File f = new File(wallpaperDir, filename);
@@ -1811,11 +1813,13 @@
SELinux.restorecon(f);
}
}
- }
- };
- BackgroundThread.getHandler().post(relabeler);
+ };
+ BackgroundThread.getHandler().post(relabeler);
+ }
}
}
+ } finally {
+ t.traceEnd();
}
}
@@ -1833,31 +1837,37 @@
}
void switchUser(int userId, IRemoteCallback reply) {
- final WallpaperData systemWallpaper;
- final WallpaperData lockWallpaper;
- synchronized (mLock) {
- if (mCurrentUserId == userId) {
- return;
+ TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+ t.traceBegin("switch-user-" + userId);
+ try {
+ final WallpaperData systemWallpaper;
+ final WallpaperData lockWallpaper;
+ synchronized (mLock) {
+ if (mCurrentUserId == userId) {
+ return;
+ }
+ mCurrentUserId = userId;
+ systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+ final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
+ lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+ // Not started watching yet, in case wallpaper data was loaded for other reasons.
+ if (systemWallpaper.wallpaperObserver == null) {
+ systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
+ systemWallpaper.wallpaperObserver.startWatching();
+ }
+ switchWallpaper(systemWallpaper, reply);
}
- mCurrentUserId = userId;
- systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
- final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
- lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
- // Not started watching yet, in case wallpaper data was loaded for other reasons.
- if (systemWallpaper.wallpaperObserver == null) {
- systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
- systemWallpaper.wallpaperObserver.startWatching();
- }
- switchWallpaper(systemWallpaper, reply);
- }
- // Offload color extraction to another thread since switchUser will be called
- // from the main thread.
- FgThread.getHandler().post(() -> {
- notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
- notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
- notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
- });
+ // Offload color extraction to another thread since switchUser will be called
+ // from the main thread.
+ FgThread.getHandler().post(() -> {
+ notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
+ notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
+ });
+ } finally {
+ t.traceEnd();
+ }
}
void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index db94cf1..9599bad 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
@@ -34,7 +32,7 @@
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
-import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.DISPLAY;
import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
import static com.android.server.am.ActivityDisplayProto.ID;
import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
@@ -52,6 +50,7 @@
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.server.wm.RootActivityContainer.FindTaskResult;
import static com.android.server.wm.RootActivityContainer.TAG_STATES;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
@@ -85,23 +84,19 @@
* Exactly one of these classes per Display in the system. Capable of holding zero or more
* attached {@link ActivityStack}s.
*/
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends DisplayContent {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
static final int POSITION_TOP = Integer.MAX_VALUE;
static final int POSITION_BOTTOM = Integer.MIN_VALUE;
-
/**
* Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
*/
private static int sNextFreeStackId = 0;
- private ActivityTaskManagerService mService;
private RootActivityContainer mRootActivityContainer;
- // TODO: Remove once unification is complete.
- DisplayContent mDisplayContent;
/** Actual Display this object tracks. */
int mDisplayId;
Display mDisplay;
@@ -111,7 +106,6 @@
* stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
* changing the list should also call {@link #onStackOrderChanged()}.
*/
- private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
/** Array of all UIDs that are present on the display. */
@@ -157,13 +151,6 @@
*/
private ActivityStack mLastFocusedStack;
- // Cached reference to some special stacks we tend to get a lot so we don't need to loop
- // through the list to find them.
- private ActivityStack mHomeStack = null;
- private ActivityStack mRecentsStack = null;
- private ActivityStack mPinnedStack = null;
- private ActivityStack mSplitScreenPrimaryStack = null;
-
// Used in updating the display size
private Point mTmpDisplaySize = new Point();
@@ -173,15 +160,24 @@
private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
ActivityDisplay(RootActivityContainer root, Display display) {
+ super(display, root.mWindowManager);
mRootActivityContainer = root;
- mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
- mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
- mDisplayContent.reconfigureDisplayLocked();
- onRequestedOverrideConfigurationChanged(
- mDisplayContent.getRequestedOverrideConfiguration());
- mService.mWindowManager.mDisplayNotificationController.dispatchDisplayAdded(this);
+
+ if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
+
+ mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+
+ if (mWmService.mDisplayManagerInternal != null) {
+ mWmService.mDisplayManagerInternal
+ .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
+ configureDisplayPolicy();
+ }
+
+ reconfigureDisplayLocked();
+ onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
}
void onDisplayChanged() {
@@ -190,7 +186,7 @@
if (displayId != DEFAULT_DISPLAY) {
final int displayState = mDisplay.getState();
if (displayState == Display.STATE_OFF && mOffToken == null) {
- mOffToken = mService.acquireSleepToken("Display-off", displayId);
+ mOffToken = mAtmService.acquireSleepToken("Display-off", displayId);
} else if (displayState == Display.STATE_ON && mOffToken != null) {
mOffToken.release();
mOffToken = null;
@@ -199,98 +195,72 @@
mDisplay.getRealSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
- if (mDisplayContent != null) {
- mDisplayContent.updateDisplayInfo();
- mService.mWindowManager.requestTraversal();
- }
+ updateDisplayInfo();
+ mWmService.requestTraversal();
}
- // TODO(display-unify): Merge with addChild below.
- void addChild(ActivityStack stack, int position) {
- addChild(stack, position, false /*fromDc*/);
+ void addStack(ActivityStack stack, int position) {
+ setStackOnDisplay(stack, position);
+ positionStackAt(stack, position);
+ mAtmService.updateSleepIfNeededLocked();
}
- void addChild(ActivityStack stack, int position, boolean fromDc) {
- boolean toTop = position == POSITION_TOP;
- if (position == POSITION_BOTTOM) {
- position = 0;
- } else if (toTop) {
- position = getChildCount();
+ void onStackRemoved(ActivityStack stack) {
+ if (DEBUG_STACK) {
+ Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
}
- if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
- + " to displayId=" + mDisplayId + " position=" + position);
- addStackReferenceIfNeeded(stack);
- if (!fromDc) {
- mDisplayContent.setStackOnDisplay(stack, position);
- }
- positionChildAt(stack, position);
- mService.updateSleepIfNeededLocked();
- }
-
- // TODO(display-unify): Merge with removeChild below.
- void onChildRemoved(ActivityStack stack) {
- if (!mStacks.remove(stack)) {
- // Stack no longer here!
- return;
- }
-
- if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
- + " from displayId=" + mDisplayId);
if (mPreferredTopFocusableStack == stack) {
mPreferredTopFocusableStack = null;
}
- removeStackReferenceIfNeeded(stack);
releaseSelfIfNeeded();
- mService.updateSleepIfNeededLocked();
+ mAtmService.updateSleepIfNeededLocked();
onStackOrderChanged(stack);
}
- void removeChild(ActivityStack stack) {
- mDisplayContent.removeStackFromDisplay(stack);
+ void positionStackAtTop(ActivityStack stack, boolean includingParents) {
+ positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
}
- void positionChildAtTop(ActivityStack stack, boolean includingParents) {
- positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
- }
-
- void positionChildAtTop(ActivityStack stack, boolean includingParents,
+ void positionStackAtTop(ActivityStack stack, boolean includingParents,
String updateLastFocusedStackReason) {
- positionChildAt(stack, getChildCount(), includingParents, updateLastFocusedStackReason);
+ positionStackAt(stack, getStackCount(), includingParents, updateLastFocusedStackReason);
}
- void positionChildAtBottom(ActivityStack stack) {
- positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
+ void positionStackAtBottom(ActivityStack stack) {
+ positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
}
- void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
- positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
+ void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
+ positionStackAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
}
- private void positionChildAt(ActivityStack stack, int position) {
- positionChildAt(stack, position, false /* includingParents */,
+ private void positionStackAt(ActivityStack stack, int position) {
+ positionStackAt(stack, position, false /* includingParents */,
null /* updateLastFocusedStackReason */);
}
- private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
+ private void positionStackAt(ActivityStack stack, int position, boolean includingParents,
String updateLastFocusedStackReason) {
// TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
// the position internally, also update the logic here
final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
? getFocusedStack() : null;
- final boolean wasContained = mStacks.remove(stack);
- if (mSingleTaskInstance && getChildCount() > 0) {
+ final boolean wasContained = getIndexOf(stack) >= 0;
+ if (mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
throw new IllegalStateException(
- "positionChildAt: Can only have one child on display=" + this);
+ "positionStackAt: Can only have one task on display=" + this);
}
- final int insertPosition = getTopInsertPosition(stack, position);
- mStacks.add(insertPosition, stack);
+
+ // Since positionChildAt() is called during the creation process of pinned stacks,
+ // ActivityStack#getStack() can be null.
+ positionStackAt(position, stack, includingParents);
// The insert position may be adjusted to non-top when there is always-on-top stack. Since
// the original position is preferred to be top, the stack should have higher priority when
// we are looking for top focusable stack. The condition {@code wasContained} restricts the
// preferred stack is set only when moving an existing stack to top instead of adding a new
// stack that may be too early (e.g. in the middle of launching or reparenting).
- if (wasContained && position >= getChildCount() - 1 && stack.isFocusableAndVisible()) {
+ if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
mPreferredTopFocusableStack = stack;
} else if (mPreferredTopFocusableStack == stack) {
mPreferredTopFocusableStack = null;
@@ -307,67 +277,14 @@
}
}
- // Since positionChildAt() is called during the creation process of pinned stacks,
- // ActivityStack#getStack() can be null.
- if (mDisplayContent != null) {
- mDisplayContent.positionStackAt(insertPosition, stack, includingParents);
- }
onStackOrderChanged(stack);
}
- private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
- int position = getChildCount();
- if (stack.inPinnedWindowingMode()) {
- // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
- // just return the candidate position.
- return Math.min(position, candidatePosition);
- }
- while (position > 0) {
- final ActivityStack targetStack = getChildAt(position - 1);
- if (!targetStack.isAlwaysOnTop()) {
- // We reached a stack that isn't always-on-top.
- break;
- }
- if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
- // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
- break;
- }
- position--;
- }
- return Math.min(position, candidatePosition);
- }
-
- <T extends ActivityStack> T getStack(int stackId) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ ActivityStack getStack(int stackId) {
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
if (stack.mStackId == stackId) {
- return (T) stack;
- }
- }
- return null;
- }
-
- /**
- * @return the topmost stack on the display that is compatible with the input windowing mode and
- * activity type. {@code null} means no compatible stack on the display.
- * @see ConfigurationContainer#isCompatible(int, int)
- */
- <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_HOME) {
- return (T) mHomeStack;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
- return (T) mRecentsStack;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- return (T) mPinnedStack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- return (T) mSplitScreenPrimaryStack;
- }
-
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
- if (stack.isCompatible(windowingMode, activityType)) {
- return (T) stack;
+ return stack;
}
}
return null;
@@ -388,10 +305,10 @@
* @see #getStack(int, int)
* @see #createStack(int, int, boolean)
*/
- <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+ ActivityStack getOrCreateStack(int windowingMode, int activityType,
boolean onTop) {
if (!alwaysCreateStack(windowingMode, activityType)) {
- T stack = getStack(windowingMode, activityType);
+ ActivityStack stack = getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
@@ -404,7 +321,7 @@
* if a compatible stack doesn't exist.
* @see #getOrCreateStack(int, int, boolean)
*/
- <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
+ ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
@Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
boolean onTop) {
// First preference is the windowing mode in the activity options if set.
@@ -433,9 +350,9 @@
* @param onTop If true the stack will be created at the top of the display, else at the bottom.
* @return The newly created stack.
*/
- <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+ ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
- if (mSingleTaskInstance && getChildCount() > 0) {
+ if (mSingleTaskInstance && getStackCount() > 0) {
// Create stack on default display instead since this display can only contain 1 stack.
// TODO: Kinda a hack, but better that having the decision at each call point. Hoping
// this goes away once ActivityView is no longer using virtual displays.
@@ -452,17 +369,17 @@
if (activityType != ACTIVITY_TYPE_STANDARD) {
// For now there can be only one stack of a particular non-standard activity type on a
// display. So, get that ignoring whatever windowing mode it is currently in.
- T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+ ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
if (stack != null) {
throw new IllegalArgumentException("Stack=" + stack + " of activityType="
+ activityType + " already on display=" + this + ". Can't have multiple.");
}
}
- if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
- mService.mSupportsSplitScreenMultiWindow,
- mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
- activityType)) {
+ if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
+ mAtmService.mSupportsSplitScreenMultiWindow,
+ mAtmService.mSupportsFreeformWindowManagement,
+ mAtmService.mSupportsPictureInPicture, activityType)) {
throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
+ windowingMode);
}
@@ -472,13 +389,13 @@
}
@VisibleForTesting
- <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+ ActivityStack createStackUnchecked(int windowingMode, int activityType,
int stackId, boolean onTop) {
if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
+ "activity type.");
}
- return (T) new ActivityStack(this, stackId,
+ return new ActivityStack(this, stackId,
mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop);
}
@@ -491,8 +408,8 @@
return mPreferredTopFocusableStack;
}
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
if (stack.isFocusableAndVisible()) {
return stack;
}
@@ -506,8 +423,8 @@
? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
ActivityStack candidate = null;
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
if (ignoreCurrent && stack == currentFocus) {
continue;
}
@@ -562,8 +479,8 @@
}
boolean allResumedActivitiesComplete() {
- for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityRecord r = getChildAt(stackNdx).getResumedActivity();
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
if (r != null && !r.isState(RESUMED)) {
return false;
}
@@ -589,8 +506,8 @@
*/
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
boolean someActivityPaused = false;
- for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null
&& (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
@@ -610,8 +527,8 @@
void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
FindTaskResult result) {
mTmpFindTaskResult.clear();
- for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
if (!r.hasCompatibleActivityType(stack)) {
if (DEBUG_TASKS) {
Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
@@ -654,8 +571,8 @@
final ArrayList<ActivityStack> stacks = new ArrayList<>();
for (int j = windowingModes.length - 1 ; j >= 0; --j) {
final int windowingMode = windowingModes[j];
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
if (!stack.isActivityTypeStandardOrUndefined()) {
continue;
}
@@ -682,8 +599,8 @@
final ArrayList<ActivityStack> stacks = new ArrayList<>();
for (int j = activityTypes.length - 1 ; j >= 0; --j) {
final int activityType = activityTypes[j];
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
if (stack.getActivityType() == activityType) {
stacks.add(stack);
}
@@ -695,67 +612,12 @@
}
}
- void onStackWindowingModeChanged(ActivityStack stack) {
- removeStackReferenceIfNeeded(stack);
- addStackReferenceIfNeeded(stack);
- }
-
- private void addStackReferenceIfNeeded(ActivityStack stack) {
- final int activityType = stack.getActivityType();
- final int windowingMode = stack.getWindowingMode();
-
- if (activityType == ACTIVITY_TYPE_HOME) {
- if (mHomeStack != null && mHomeStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
- + mHomeStack + " already exist on display=" + this + " stack=" + stack);
- }
- mHomeStack = stack;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
- if (mRecentsStack != null && mRecentsStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
- + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
- }
- mRecentsStack = stack;
- }
- if (windowingMode == WINDOWING_MODE_PINNED) {
- if (mPinnedStack != null && mPinnedStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
- + mPinnedStack + " already exist on display=" + this
- + " stack=" + stack);
- }
- mPinnedStack = stack;
- } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
- throw new IllegalArgumentException("addStackReferenceIfNeeded:"
- + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
- + " already exist on display=" + this + " stack=" + stack);
- }
- mSplitScreenPrimaryStack = stack;
- onSplitScreenModeActivated();
- }
- }
-
- private void removeStackReferenceIfNeeded(ActivityStack stack) {
- if (stack == mHomeStack) {
- mHomeStack = null;
- } else if (stack == mRecentsStack) {
- mRecentsStack = null;
- } else if (stack == mPinnedStack) {
- mPinnedStack = null;
- } else if (stack == mSplitScreenPrimaryStack) {
- mSplitScreenPrimaryStack = null;
- // Inform the reset of the system that split-screen mode was dismissed so things like
- // resizing all the other stacks can take place.
- onSplitScreenModeDismissed();
- }
- }
-
- private void onSplitScreenModeDismissed() {
- mService.deferWindowLayout();
+ void onSplitScreenModeDismissed() {
+ mAtmService.deferWindowLayout();
try {
// Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = getStackAt(i);
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
@@ -766,26 +628,28 @@
} finally {
final ActivityStack topFullscreenStack =
getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
- if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
+ final ActivityStack homeStack = getHomeStack();
+ if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
// Whenever split-screen is dismissed we want the home stack directly behind the
// current top fullscreen stack so it shows up when the top stack is finished.
// TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
// ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
// once we have that.
- mHomeStack.moveToFront("onSplitScreenModeDismissed");
+ homeStack.moveToFront("onSplitScreenModeDismissed");
topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
}
- mService.continueWindowLayout();
+ mAtmService.continueWindowLayout();
}
}
- private void onSplitScreenModeActivated() {
- mService.deferWindowLayout();
+ void onSplitScreenModeActivated() {
+ mAtmService.deferWindowLayout();
try {
// Adjust the windowing mode of any affected by split-screen to split-screen secondary.
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = getChildAt(i);
- if (otherStack == mSplitScreenPrimaryStack
+ final ActivityStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = getStackAt(i);
+ if (otherStack == splitScreenPrimaryStack
|| !otherStack.affectedBySplitScreenResize()) {
continue;
}
@@ -795,7 +659,7 @@
false /* creating */);
}
} finally {
- mService.continueWindowLayout();
+ mAtmService.continueWindowLayout();
}
}
@@ -890,10 +754,10 @@
int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
int activityType) {
// Make sure the windowing mode we are trying to use makes sense for what is supported.
- boolean supportsMultiWindow = mService.mSupportsMultiWindow;
- boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
- boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
- boolean supportsPip = mService.mSupportsPictureInPicture;
+ boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
+ boolean supportsSplitScreen = mAtmService.mSupportsSplitScreenMultiWindow;
+ boolean supportsFreeform = mAtmService.mSupportsFreeformWindowManagement;
+ boolean supportsPip = mAtmService.mSupportsPictureInPicture;
if (supportsMultiWindow) {
if (task != null) {
supportsMultiWindow = task.isResizeable();
@@ -927,21 +791,13 @@
return WINDOWING_MODE_UNDEFINED;
}
- /**
- * Get the topmost stack on the display. It may be different from focused stack, because
- * some stacks are not focusable (e.g. PiP).
- */
- ActivityStack getTopStack() {
- return mStacks.isEmpty() ? null : getChildAt(getChildCount() - 1);
- }
-
boolean isTopStack(ActivityStack stack) {
return stack == getTopStack();
}
boolean isTopNotPinnedStack(ActivityStack stack) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack current = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack current = getStackAt(i);
if (!current.inPinnedWindowingMode()) {
return current == stack;
}
@@ -950,8 +806,8 @@
}
ActivityStack getTopStackInWindowingMode(int windowingMode) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack current = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack current = getStackAt(i);
if (windowingMode == current.getWindowingMode()) {
return current;
}
@@ -981,8 +837,8 @@
// Look in other focusable stacks.
if (topRunning == null) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
// Only consider focusable stacks other than the current focused one.
if (stack == focusedStack || !stack.isFocusable()) {
continue;
@@ -1005,22 +861,18 @@
return topRunning;
}
- int getIndexOf(ActivityStack stack) {
- return mStacks.indexOf(stack);
- }
-
boolean updateDisplayOverrideConfigurationLocked() {
Configuration values = new Configuration();
- mDisplayContent.computeScreenConfiguration(values);
+ computeScreenConfiguration(values);
- mService.mH.sendMessage(PooledLambda.obtainMessage(
- ActivityManagerInternal::updateOomLevelsForDisplay, mService.mAmInternal,
+ mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
+ ActivityManagerInternal::updateOomLevelsForDisplay, mAtmService.mAmInternal,
mDisplayId));
Settings.System.clearConfiguration(values);
updateDisplayOverrideConfigurationLocked(values, null /* starting */,
- false /* deferResume */, mService.mTmpUpdateConfigurationResult);
- return mService.mTmpUpdateConfigurationResult.changes != 0;
+ false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
+ return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
}
/**
@@ -1034,14 +886,14 @@
int changes = 0;
boolean kept = true;
- mService.deferWindowLayout();
+ mAtmService.deferWindowLayout();
try {
if (values != null) {
if (mDisplayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config, so
// we're calling global config update instead for default display. It will also
// apply the correct override config.
- changes = mService.updateGlobalConfigurationLocked(values,
+ changes = mAtmService.updateGlobalConfigurationLocked(values,
false /* initLocale */, false /* persistent */,
UserHandle.USER_NULL /* userId */, deferResume);
} else {
@@ -1049,9 +901,9 @@
}
}
- kept = mService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+ kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
} finally {
- mService.continueWindowLayout();
+ mAtmService.continueWindowLayout();
}
if (result != null) {
@@ -1071,17 +923,17 @@
final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange && mDisplayId == DEFAULT_DISPLAY) {
- mService.mAppWarnings.onDensityChanged();
+ mAtmService.mAppWarnings.onDensityChanged();
// Post message to start process to avoid possible deadlock of calling into AMS with
// the ATMS lock held.
final Message msg = PooledLambda.obtainMessage(
ActivityManagerInternal::killAllBackgroundProcessesExcept,
- mService.mAmInternal, N,
+ mAtmService.mAmInternal, N,
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
- mService.mH.sendMessage(msg);
+ mAtmService.mH.sendMessage(msg);
}
- mService.mWindowManager.mDisplayNotificationController.dispatchDisplayChanged(
+ mWmService.mDisplayNotificationController.dispatchDisplayChanged(
this, getConfiguration());
}
return changes;
@@ -1092,43 +944,29 @@
final int currRotation =
getRequestedOverrideConfiguration().windowConfiguration.getRotation();
if (currRotation != ROTATION_UNDEFINED
- && currRotation != overrideConfiguration.windowConfiguration.getRotation()
- && mDisplayContent != null) {
- mDisplayContent.applyRotationLocked(currRotation,
+ && currRotation != overrideConfiguration.windowConfiguration.getRotation()) {
+ applyRotationLocked(currRotation,
overrideConfiguration.windowConfiguration.getRotation());
}
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
- if (mDisplayContent != null) {
- mService.mWindowManager.setNewDisplayOverrideConfiguration(
- overrideConfiguration, mDisplayContent);
- }
- mService.addWindowLayoutReasons(
+ mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
+ mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
// update resources before cascade so that docked/pinned stacks use the correct info
- if (mDisplayContent != null) {
- mDisplayContent.preOnConfigurationChanged();
- }
+ preOnConfigurationChanged();
super.onConfigurationChanged(newParentConfig);
}
void onLockTaskPackagesUpdated() {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- getChildAt(i).onLockTaskPackagesUpdated();
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ getStackAt(i).onLockTaskPackagesUpdated();
}
}
- /** We are in the process of exiting split-screen mode. */
- void onExitingSplitScreenMode() {
- // Remove reference to the primary-split-screen stack so it no longer has any effect on the
- // display. For example, we want to be able to create fullscreen stack for standard activity
- // types when exiting split-screen mode.
- mSplitScreenPrimaryStack = null;
- }
-
/** Checks whether the given activity is in size compatibility mode and notifies the change. */
void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
@@ -1137,7 +975,7 @@
}
if (!r.inSizeCompatMode()) {
if (mLastCompatModeActivity != null) {
- mService.getTaskChangeNotificationController()
+ mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
}
mLastCompatModeActivity = null;
@@ -1147,44 +985,13 @@
return;
}
mLastCompatModeActivity = r;
- mService.getTaskChangeNotificationController()
+ mAtmService.getTaskChangeNotificationController()
.notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
}
- ActivityStack getSplitScreenPrimaryStack() {
- return mSplitScreenPrimaryStack;
- }
-
- boolean hasSplitScreenPrimaryStack() {
- return mSplitScreenPrimaryStack != null;
- }
-
- ActivityStack getPinnedStack() {
- return mPinnedStack;
- }
-
- boolean hasPinnedStack() {
- return mPinnedStack != null;
- }
-
@Override
public String toString() {
- return "ActivityDisplay={" + mDisplayId + " numStacks=" + getChildCount() + "}";
- }
-
- @Override
- protected int getChildCount() {
- return mStacks.size();
- }
-
- @Override
- protected ActivityStack getChildAt(int index) {
- return mStacks.get(index);
- }
-
- @Override
- protected ConfigurationContainer getParent() {
- return mRootActivityContainer;
+ return "ActivityDisplay={" + mDisplayId + " numStacks=" + getStackCount() + "}";
}
boolean isPrivate() {
@@ -1227,10 +1034,10 @@
final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
mRootActivityContainer.mStackSupervisor.beginDeferResume();
try {
- int numStacks = getChildCount();
+ int numStacks = getStackCount();
// Keep the order from bottom to top.
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack stack = getChildAt(stackNdx);
+ final ActivityStack stack = getStackAt(stackNdx);
// Always finish non-standard type stacks.
if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
stack.finishAllActivitiesImmediately();
@@ -1241,14 +1048,14 @@
final int windowingMode = toDisplay.hasSplitScreenPrimaryStack()
? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
: WINDOWING_MODE_UNDEFINED;
- stack.reparent(toDisplay.mDisplayContent, true /* onTop */);
+ stack.reparent(toDisplay, true /* onTop */);
stack.setWindowingMode(windowingMode);
lastReparentedStack = stack;
}
// Stacks may be removed from this display. Ensure each stack will be processed and
// the loop will end.
- stackNdx -= numStacks - getChildCount();
- numStacks = getChildCount();
+ stackNdx -= numStacks - getStackCount();
+ numStacks = getStackCount();
}
} finally {
mRootActivityContainer.mStackSupervisor.endDeferResume();
@@ -1265,24 +1072,23 @@
if (!mAllSleepTokens.isEmpty()) {
mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
mAllSleepTokens.clear();
- mService.updateSleepIfNeededLocked();
+ mAtmService.updateSleepIfNeededLocked();
}
}
private void releaseSelfIfNeeded() {
- if (!mRemoved || mDisplayContent == null) {
+ if (!mRemoved) {
return;
}
- final ActivityStack stack = getChildCount() == 1 ? getChildAt(0) : null;
+ final ActivityStack stack = getStackCount() == 1 ? getStackAt(0) : null;
if (stack != null && stack.isActivityTypeHome() && stack.getAllTasks().isEmpty()) {
// Release this display if an empty home stack is the only thing left.
// Since it is the last stack, this display will be released along with the stack
// removal.
stack.removeIfPossible();
- } else if (mStacks.isEmpty()) {
- mDisplayContent.removeIfPossible();
- mDisplayContent = null;
+ } else if (getTopStack() == null) {
+ removeIfPossible();
mRootActivityContainer.removeChild(this);
mRootActivityContainer.mStackSupervisor
.getKeyguardController().onDisplayRemoved(mDisplayId);
@@ -1302,14 +1108,6 @@
private static void addActivityUid(ActivityRecord r, IntArray uids) {
uids.add(r.getUid());
}
- /**
- * Checks if system decorations should be shown on this display.
- *
- * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
- */
- boolean supportsSystemDecorations() {
- return mDisplayContent.supportsSystemDecorations();
- }
@VisibleForTesting
boolean shouldDestroyContentOnRemove() {
@@ -1317,14 +1115,11 @@
}
boolean shouldSleep() {
- return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
- && (mService.mRunningVoice == null);
+ return (getStackCount() == 0 || !mAllSleepTokens.isEmpty())
+ && (mAtmService.mRunningVoice == null);
}
void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
- if (mDisplayContent == null) {
- return;
- }
final ActivityRecord newFocus;
final IBinder token = r.appToken;
if (token == null) {
@@ -1332,7 +1127,7 @@
mDisplayId);
newFocus = null;
} else {
- newFocus = mService.mWindowManager.mRoot.getActivityRecord(token);
+ newFocus = mWmService.mRoot.getActivityRecord(token);
if (newFocus == null) {
Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+ ", displayId=" + mDisplayId);
@@ -1342,9 +1137,9 @@
moveFocusNow, mDisplayId);
}
- final boolean changed = mDisplayContent.setFocusedApp(newFocus);
+ final boolean changed = setFocusedApp(newFocus);
if (moveFocusNow && changed) {
- mService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+ mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
true /*updateInputWindows*/);
}
}
@@ -1354,8 +1149,8 @@
* already top-most.
*/
ActivityStack getStackAbove(ActivityStack stack) {
- final int stackIndex = mStacks.indexOf(stack) + 1;
- return (stackIndex < getChildCount()) ? getChildAt(stackIndex) : null;
+ final int stackIndex = getIndexOf(stack) + 1;
+ return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
}
/**
@@ -1369,12 +1164,12 @@
}
// Move the stack to the bottom to not affect the following visibility checks
- positionChildAtBottom(stack);
+ positionStackAtBottom(stack);
// Find the next position where the stack should be placed
- final int numStacks = getChildCount();
+ final int numStacks = getStackCount();
for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
- final ActivityStack s = getChildAt(stackNdx);
+ final ActivityStack s = getStackAt(stackNdx);
if (s == stack) {
continue;
}
@@ -1383,7 +1178,7 @@
winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
if (s.shouldBeVisible(null) && isValidWindowingMode) {
// Move the provided stack to behind this stack
- positionChildAt(stack, Math.max(0, stackNdx - 1));
+ positionStackAt(stack, Math.max(0, stackNdx - 1));
break;
}
}
@@ -1403,25 +1198,26 @@
// list, so we need to adjust the insertion index to account for the removed index
// TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
// position internally
- final int stackIndex = mStacks.indexOf(stack);
- final int behindStackIndex = mStacks.indexOf(behindStack);
+ final int stackIndex = getIndexOf(stack);
+ final int behindStackIndex = getIndexOf(behindStack);
final int insertIndex = stackIndex <= behindStackIndex
? behindStackIndex - 1 : behindStackIndex;
- positionChildAt(stack, Math.max(0, insertIndex));
+ positionStackAt(stack, Math.max(0, insertIndex));
}
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
- for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
}
}
void moveHomeStackToFront(String reason) {
- if (mHomeStack != null) {
- mHomeStack.moveToFront(reason);
+ final ActivityStack homeStack = getHomeStack();
+ if (homeStack != null) {
+ homeStack.moveToFront(reason);
}
}
@@ -1439,25 +1235,21 @@
}
@Nullable
- ActivityStack getHomeStack() {
- return mHomeStack;
- }
-
- @Nullable
ActivityRecord getHomeActivity() {
return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
}
@Nullable
ActivityRecord getHomeActivityForUser(int userId) {
- if (mHomeStack == null) {
+ final ActivityStack homeStack = getHomeStack();
+ if (homeStack == null) {
return null;
}
final PooledPredicate p = PooledLambda.obtainPredicate(
ActivityDisplay::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
userId);
- final ActivityRecord r = mHomeStack.getActivity(p);
+ final ActivityRecord r = homeStack.getActivity(p);
p.recycle();
return r;
}
@@ -1502,32 +1294,14 @@
}
}
- /**
- * See {@link DisplayContent#deferUpdateImeTarget()}
- */
- public void deferUpdateImeTarget() {
- if (mDisplayContent != null) {
- mDisplayContent.deferUpdateImeTarget();
- }
- }
-
- /**
- * See {@link DisplayContent#deferUpdateImeTarget()}
- */
- public void continueUpdateImeTarget() {
- if (mDisplayContent != null) {
- mDisplayContent.continueUpdateImeTarget();
- }
- }
-
void setDisplayToSingleTaskInstance() {
- final int childCount = getChildCount();
+ final int childCount = getStackCount();
if (childCount > 1) {
throw new IllegalArgumentException("Display already has multiple stacks. display="
+ this);
}
if (childCount > 0) {
- final ActivityStack stack = getChildAt(0);
+ final ActivityStack stack = getStackAt(0);
if (stack.getChildCount() > 1) {
throw new IllegalArgumentException("Display stack already has multiple tasks."
+ " display=" + this + " stack=" + stack);
@@ -1544,8 +1318,8 @@
@VisibleForTesting
void removeAllTasks() {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = getChildAt(i);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getStackAt(i);
final ArrayList<Task> tasks = stack.getAllTasks();
for (int j = tasks.size() - 1; j >= 0; --j) {
stack.removeChild(tasks.get(j), "removeAllTasks");
@@ -1554,20 +1328,24 @@
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + getChildCount()
+ pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + getStackCount()
+ (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
final String myPrefix = prefix + " ";
- if (mHomeStack != null) {
- pw.println(myPrefix + "mHomeStack=" + mHomeStack);
+ ActivityStack stack = getHomeStack();
+ if (stack != null) {
+ pw.println(myPrefix + "mHomeStack=" + stack);
}
- if (mRecentsStack != null) {
- pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
+ stack = getRecentsStack();
+ if (stack != null) {
+ pw.println(myPrefix + "mRecentsStack=" + stack);
}
- if (mPinnedStack != null) {
- pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
+ stack = getPinnedStack();
+ if (stack != null) {
+ pw.println(myPrefix + "mPinnedStack=" + stack);
}
- if (mSplitScreenPrimaryStack != null) {
- pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
+ stack = getSplitScreenPrimaryStack();
+ if (stack != null) {
+ pw.println(myPrefix + "mSplitScreenPrimaryStack=" + stack);
}
if (mPreferredTopFocusableStack != null) {
pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
@@ -1578,8 +1356,8 @@
}
public void dumpStacks(PrintWriter pw) {
- for (int i = getChildCount() - 1; i >= 0; --i) {
- pw.print(getChildAt(i).mStackId);
+ for (int i = getStackCount() - 1; i >= 0; --i) {
+ pw.print(getStackAt(i).mStackId);
if (i > 0) {
pw.print(",");
}
@@ -1589,7 +1367,7 @@
public void writeToProto(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
final long token = proto.start(fieldId);
- super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
+ writeToProtoInner(proto, DISPLAY, logLevel);
proto.write(ID, mDisplayId);
proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
final ActivityStack focusedStack = getFocusedStack();
@@ -1602,8 +1380,8 @@
} else {
proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
}
- for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = getChildAt(stackNdx);
+ for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getStackAt(stackNdx);
stack.writeToProto(proto, STACKS, logLevel);
}
proto.end(token);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 963e090..5377db4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2237,7 +2237,7 @@
}
}
return (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable())
- && getParent() != null;
+ && getDisplay() != null;
}
/** Move activity with its stack to front and make the stack focused. */
@@ -2256,7 +2256,8 @@
return false;
}
- if (mRootActivityContainer.getTopResumedActivity() == this) {
+ if (mRootActivityContainer.getTopResumedActivity() == this
+ && getDisplayContent().mFocusedApp == this) {
if (DEBUG_FOCUS) {
Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
}
@@ -2420,7 +2421,7 @@
final ActivityDisplay display = stack.getDisplay();
next = display.topRunningActivity();
if (next != null) {
- display.positionChildAtTop(next.getActivityStack(),
+ display.positionStackAtTop(next.getActivityStack(),
false /* includingParents */, "finish-display-top");
}
}
@@ -5688,8 +5689,6 @@
@Override
boolean isWaitingForTransitionStart() {
final DisplayContent dc = getDisplayContent();
- // TODO(display-unify): Test for null can be removed once unification is done.
- if (dc == null) return false;
return dc.mAppTransition.isTransitionSet()
&& (dc.mOpeningApps.contains(this)
|| dc.mClosingApps.contains(this)
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bb3126b..918d40b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -750,7 +750,7 @@
// stacks on a wrong display.
mDisplayId = display.mDisplayId;
setActivityType(activityType);
- display.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
+ display.addStack(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
true /* creating */);
@@ -879,7 +879,6 @@
newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
hasNewOverrideBounds = true;
}
- display.onStackWindowingModeChanged(this);
}
if (hasNewOverrideBounds) {
if (inSplitScreenPrimaryWindowingMode()) {
@@ -896,7 +895,7 @@
// Since always on top is only on when the stack is freeform or pinned, the state
// can be toggled when the windowing mode changes. We must make sure the stack is
// placed properly when always on top state changes.
- display.positionChildAtTop(this, false /* includingParents */);
+ display.positionStackAtTop(this, false /* includingParents */);
}
}
@@ -1337,7 +1336,7 @@
}
final boolean movingTask = task != null;
- display.positionChildAtTop(this, !movingTask /* includingParents */, reason);
+ display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
if (movingTask) {
// This also moves the entire hierarchy branch to top, including parents
positionChildAtTop(task);
@@ -1353,7 +1352,7 @@
return;
}
- getDisplay().positionChildAtBottom(this, reason);
+ getDisplay().positionStackAtBottom(this, reason);
if (task != null) {
positionChildAtBottom(task);
}
@@ -1848,8 +1847,8 @@
boolean shouldBeVisible = true;
final int windowingMode = getWindowingMode();
final boolean isAssistantType = isActivityTypeAssistant();
- for (int i = display.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack other = display.getChildAt(i);
+ for (int i = display.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack other = display.getStackAt(i);
final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
if (other == this) {
// Should be visible if there is no other stack occluding it, unless it doesn't
@@ -3010,7 +3009,7 @@
if (index == 0) {
return false;
}
- final ActivityStack stackBehind = display.getChildAt(index - 1);
+ final ActivityStack stackBehind = display.getStackAt(index - 1);
return stackBehind.isActivityTypeStandard();
}
@@ -3768,7 +3767,7 @@
if (!hasChild()) {
// Stack is now empty...
- removeIfPossible();
+ removeIfPossible();
}
moveHomeStackToFrontIfNeeded(topFocused, display, reason);
@@ -3887,7 +3886,7 @@
// always on top windows. Since the position the stack should be inserted into is calculated
// properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
// request that the stack is put at top here.
- display.positionChildAtTop(this, false /* includingParents */);
+ display.positionStackAtTop(this, false /* includingParents */);
}
/** NOTE: Should only be called from {@link Task#reparent}. */
@@ -4019,7 +4018,7 @@
final Task task = mChildren.get(0);
setWindowingMode(WINDOWING_MODE_UNDEFINED);
- getDisplay().positionChildAtTop(this, false /* includingParents */);
+ getDisplay().positionStackAtTop(this, false /* includingParents */);
mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
@@ -4456,20 +4455,14 @@
}
}
- // TODO(display-unify): Remove after display unification.
- protected void onParentChanged(ActivityDisplay newParent, ActivityDisplay oldParent) {
- onParentChanged(
- newParent != null ? newParent.mDisplayContent : null,
- oldParent != null ? oldParent.mDisplayContent : null);
- }
-
@Override
protected void onParentChanged(
ConfigurationContainer newParent, ConfigurationContainer oldParent) {
+ // TODO(display-merge): Remove cast
final ActivityDisplay display = newParent != null
- ? ((WindowContainer) newParent).getDisplayContent().mActivityDisplay : null;
+ ? (ActivityDisplay) ((WindowContainer) newParent).getDisplayContent() : null;
final ActivityDisplay oldDisplay = oldParent != null
- ? ((WindowContainer) oldParent).getDisplayContent().mActivityDisplay : null;
+ ? (ActivityDisplay) ((WindowContainer) oldParent).getDisplayContent() : null;
mDisplayId = (display != null) ? display.mDisplayId : INVALID_DISPLAY;
mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d98aa6f..7356368 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1500,13 +1500,11 @@
mRootActivityContainer.getActivityDisplay(toDisplayId);
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- // Tell the display we are exiting split-screen mode.
- toDisplay.onExitingSplitScreenMode();
// We are moving all tasks from the docked stack to the fullscreen stack,
// which is dismissing the docked stack, so resize all other stacks to
// fullscreen here already so we don't end up with resize trashing.
- for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack otherStack = toDisplay.getChildAt(i);
+ for (int i = toDisplay.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack otherStack = toDisplay.getStackAt(i);
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
@@ -1654,8 +1652,8 @@
// screen controls and is also the same for all stacks.
final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
final Rect otherTaskRect = new Rect();
- for (int i = display.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack current = display.getChildAt(i);
+ for (int i = display.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack current = display.getStackAt(i);
if (!current.inSplitScreenSecondaryWindowingMode()) {
continue;
}
@@ -1740,7 +1738,7 @@
* to the fullscreen stack. This is to guarantee that when we are removing a stack,
* that the client receives onStop() before it is reparented. We do this by detaching
* the stack from the display so that it will be considered invisible when
- * ensureActivitiesVisible() is called, and all of its activitys will be marked
+ * ensureActivitiesVisible() is called, and all of its activities will be marked
* invisible as well and added to the stopping list. After which we process the
* stopping list by handling the idle.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8164bf4..baa2955 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1132,7 +1132,7 @@
request.outActivity[0] = mLastStartActivityRecord;
}
- return getExternalResult(mLastStartActivityResult);
+ return mLastStartActivityResult;
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b046b08..2aa9ea5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -138,7 +138,6 @@
import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
import android.animation.AnimationHandler;
-import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -221,7 +220,7 @@
* particular Display.
*/
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
- implements WindowManagerPolicy.DisplayContentInfo, ConfigurationContainerListener {
+ implements WindowManagerPolicy.DisplayContentInfo {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
/** The default scaling mode that scales content automatically. */
@@ -236,11 +235,10 @@
@Retention(RetentionPolicy.SOURCE)
@interface ForceScalingMode {}
- /** Unique identifier of this stack. */
- private final int mDisplayId;
+ ActivityTaskManagerService mAtmService;
- // TODO: Remove once unification is complete.
- ActivityDisplay mActivityDisplay;
+ /** Unique identifier of this display. */
+ private final int mDisplayId;
/** The containers below are the only child containers the display can have. */
// Contains all window containers that are related to apps (Activities)
@@ -671,10 +669,12 @@
getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mLayoutSeq;
- // If this is the first layout, we need to initialize the last inset values as
- // otherwise we'd immediately cause an unnecessary resize.
+ // If this is the first layout, we need to initialize the last frames and inset values,
+ // as otherwise we'd immediately cause an unnecessary resize.
if (firstLayout) {
+ w.updateLastFrames();
w.updateLastInsetValues();
+ w.updateLocationInParentDisplayIfNeeded();
}
if (w.mActivityRecord != null) {
@@ -842,16 +842,15 @@
* @param service You know.
* @param activityDisplay The ActivityDisplay for the display container.
*/
- DisplayContent(Display display, WindowManagerService service,
- ActivityDisplay activityDisplay) {
+ DisplayContent(Display display, WindowManagerService service) {
super(service);
- mActivityDisplay = activityDisplay;
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
+ " new=" + display);
}
+ mAtmService = mWmService.mAtmService;
mDisplay = display;
mDisplayId = display.getDisplayId();
mWallpaperController = new WallpaperController(mWmService, this);
@@ -1125,20 +1124,6 @@
mAppTransitionController.registerRemoteAnimations(definition);
}
- /**
- * The display content may have configuration set from {@link #DisplayWindowSettings}. This
- * callback let the owner of container know there is existing configuration to prevent the
- * values from being replaced by the initializing {@link #ActivityDisplay}.
- */
- void initializeDisplayOverrideConfiguration() {
- if (mActivityDisplay == null) {
- return;
- }
- mActivityDisplay.onRequestedOverrideConfigurationChanged(
- getResolvedOverrideConfiguration());
- mActivityDisplay.registerConfigurationChangeListener(this);
- }
-
void reconfigureDisplayLocked() {
if (!isReady()) {
return;
@@ -1162,13 +1147,15 @@
}
void sendNewConfiguration() {
- if (!isReady() || mActivityDisplay == null) {
+ if (!isReady()) {
return;
}
if (mDisplayRotation.isWaitingForRemoteRotation()) {
return;
}
- final boolean configUpdated = mActivityDisplay.updateDisplayOverrideConfigurationLocked();
+ // TODO(display-merge): Remove cast
+ final boolean configUpdated =
+ ((ActivityDisplay) this).updateDisplayOverrideConfigurationLocked();
if (configUpdated) {
return;
}
@@ -1199,7 +1186,8 @@
if (handled && requestingContainer instanceof ActivityRecord) {
final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
- final boolean kept = mActivityDisplay.updateDisplayOverrideConfigurationLocked(
+ // TODO(display-merge): Remove cast
+ final boolean kept = ((ActivityDisplay) this).updateDisplayOverrideConfigurationLocked(
config, activityRecord, false /* deferResume */, null /* result */);
activityRecord.frozenBeforeDestroy = true;
if (!kept) {
@@ -1208,7 +1196,8 @@
} else {
// We have a new configuration to push so we need to update ATMS for now.
// TODO: Clean up display configuration push between ATMS and WMS after unification.
- mActivityDisplay.updateDisplayOverrideConfigurationLocked(
+ // TODO(display-merge): Remove cast
+ ((ActivityDisplay) this.mDisplayContent).updateDisplayOverrideConfigurationLocked(
config, null /* starting */, false /* deferResume */, null);
}
return handled;
@@ -1797,12 +1786,15 @@
return mTaskStackContainers.getHomeStack();
}
+ ActivityStack getRecentsStack() {
+ return mTaskStackContainers.getRecentsStack();
+ }
+
/**
- * @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise.
+ * @return The primary split-screen stack, and {@code null} otherwise.
*/
ActivityStack getSplitScreenPrimaryStack() {
- ActivityStack stack = mTaskStackContainers.getSplitScreenPrimaryStack();
- return (stack != null && stack.isVisible()) ? stack : null;
+ return mTaskStackContainers.getSplitScreenPrimaryStack();
}
boolean hasSplitScreenPrimaryStack() {
@@ -1841,6 +1833,22 @@
return mTaskStackContainers.getStack(windowingMode, activityType);
}
+ protected int getStackCount() {
+ return mTaskStackContainers.mChildren.size();
+ }
+
+ protected ActivityStack getStackAt(int index) {
+ return mTaskStackContainers.mChildren.get(index);
+ }
+
+ int getIndexOf(ActivityStack stack) {
+ return mTaskStackContainers.getIndexOf(stack);
+ }
+
+ void removeStack(ActivityStack stack) {
+ mTaskStackContainers.removeChild(stack);
+ }
+
@VisibleForTesting
WindowList<ActivityStack> getStacks() {
return mTaskStackContainers.mChildren;
@@ -2208,11 +2216,6 @@
stack.reparent(mTaskStackContainers, onTop ? POSITION_TOP: POSITION_BOTTOM);
}
- // TODO(display-unify): No longer needed then.
- void removeStackFromDisplay(ActivityStack stack) {
- mTaskStackContainers.removeChild(stack);
- }
-
@Override
protected void addChild(DisplayChildWindowContainer child,
Comparator<DisplayChildWindowContainer> comparator) {
@@ -2364,9 +2367,6 @@
void removeImmediately() {
mRemovingDisplay = true;
try {
- if (mActivityDisplay != null) {
- mActivityDisplay.unregisterConfigurationChangeListener(this);
- }
if (mParentWindow != null) {
mParentWindow.removeEmbeddedDisplayContent(this);
}
@@ -2386,7 +2386,9 @@
mWindowingLayer.release();
mOverlayLayer.release();
mInputMonitor.onDisplayRemoved();
- mWmService.mDisplayNotificationController.dispatchDisplayRemoved(mActivityDisplay);
+ // TODO(display-merge): Remove cast
+ mWmService.mDisplayNotificationController
+ .dispatchDisplayRemoved((ActivityDisplay) this);
} finally {
mDisplayReady = false;
mRemovingDisplay = false;
@@ -2599,9 +2601,8 @@
}
}
- @CallSuper
- @Override
- public void writeToProto(ProtoOutputStream proto, long fieldId,
+ // TODO(proto-merge): Remove once protos for ActivityDisplay and DisplayContent are merged.
+ public void writeToProtoInner(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
// Critical log level logs only visible elements to mitigate performance overheard
if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
@@ -3929,6 +3930,7 @@
// Cached reference to some special stacks we tend to get a lot so we don't need to loop
// through the list to find them.
private ActivityStack mHomeStack = null;
+ private ActivityStack mRecentsStack = null;
private ActivityStack mPinnedStack = null;
private ActivityStack mSplitScreenPrimaryStack = null;
@@ -3936,12 +3938,6 @@
super(service);
}
- @Override
- public void onConfigurationChanged(Configuration newParentConfig) {
- // TODO(display-unify): Remove after unification.
- onConfigurationChanged(newParentConfig, mActivityDisplay == null /*forwardToChildren*/);
- }
-
/**
* Returns the topmost stack on the display that is compatible with the input windowing mode
* and activity type. Null is no compatible stack on the display.
@@ -3976,6 +3972,10 @@
? mTaskStackContainers.getChildAt(mTaskStackContainers.getChildCount() - 1) : null;
}
+ int getIndexOf(ActivityStack stack) {
+ return mTaskStackContainers.mChildren.indexOf(stack);
+ }
+
ActivityStack getHomeStack() {
if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
@@ -3983,6 +3983,10 @@
return mHomeStack;
}
+ ActivityStack getRecentsStack() {
+ return mRecentsStack;
+ }
+
ActivityStack getPinnedStack() {
return mPinnedStack;
}
@@ -4018,6 +4022,13 @@
}
mHomeStack = stack;
+ } else if (stack.isActivityTypeRecents()) {
+ if (mRecentsStack != null && mRecentsStack != stack) {
+ throw new IllegalArgumentException(
+ "addStackReferenceIfNeeded: recents stack=" + mRecentsStack
+ + " already exist on display=" + this + " stack=" + stack);
+ }
+ mRecentsStack = stack;
}
final int windowingMode = stack.getWindowingMode();
if (windowingMode == WINDOWING_MODE_PINNED) {
@@ -4034,17 +4045,23 @@
+ " already exist on display=" + this + " stack=" + stack);
}
mSplitScreenPrimaryStack = stack;
+ // TODO(display-merge): Remove cast
+ ((ActivityDisplay) this.mDisplayContent).onSplitScreenModeActivated();
mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
}
- private void removeStackReferenceIfNeeded(ActivityStack stack) {
+ void removeStackReferenceIfNeeded(ActivityStack stack) {
if (stack == mHomeStack) {
mHomeStack = null;
+ } else if (stack == mRecentsStack) {
+ mRecentsStack = null;
} else if (stack == mPinnedStack) {
mPinnedStack = null;
} else if (stack == mSplitScreenPrimaryStack) {
mSplitScreenPrimaryStack = null;
+ // TODO(display-merge): Remove cast
+ ((ActivityDisplay) this.mDisplayContent).onSplitScreenModeDismissed();
// Re-set the split-screen create mode whenever the split-screen stack is removed.
mWmService.setDockedStackCreateStateLocked(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
@@ -4058,9 +4075,6 @@
position = findPositionForStack(position, stack, true /* adding */);
super.addChild(stack, position);
- if (mActivityDisplay != null) {
- mActivityDisplay.addChild(stack, position, true /*fromDc*/);
- }
// The reparenting case is handled in WindowContainer.
if (!stack.mReparenting) {
@@ -4072,9 +4086,8 @@
@Override
protected void removeChild(ActivityStack stack) {
super.removeChild(stack);
- if (mActivityDisplay != null) {
- mActivityDisplay.onChildRemoved(stack);
- }
+ // TODO(display-merge): Remove cast
+ ((ActivityDisplay) this.mDisplayContent).onStackRemoved(stack);
removeStackReferenceIfNeeded(stack);
}
@@ -4087,7 +4100,7 @@
@Override
void positionChildAt(int position, ActivityStack child, boolean includingParents) {
if (child.getWindowConfiguration().isAlwaysOnTop()
- && position != POSITION_TOP) {
+ && position != POSITION_TOP && position != mChildren.size()) {
// This stack is always-on-top, override the default behavior.
Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
@@ -4097,7 +4110,11 @@
super.positionChildAt(currentPosition, child, false /* includingParents */);
return;
}
-
+ // We don't allow untrusted display to top when task stack moves to top,
+ // until user tapping this display to change display position as top intentionally.
+ if (isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
+ includingParents = false;
+ }
final int targetPosition = findPositionForStack(position, child, false /* adding */);
super.positionChildAt(targetPosition, child, includingParents);
@@ -4133,7 +4150,12 @@
final int topChildPosition = mChildren.size() - 1;
int belowAlwaysOnTopPosition = POSITION_BOTTOM;
for (int i = topChildPosition; i >= 0; --i) {
- if (getStacks().get(i) != stack && !getStacks().get(i).isAlwaysOnTop()) {
+ // Since a stack could be repositioned while being one of the child, return
+ // current index if that's the same stack we are positioning and it is always on
+ // top.
+ final boolean sameStack = getStacks().get(i) == stack;
+ if ((sameStack && stack.isAlwaysOnTop())
+ || (!sameStack && !getStacks().get(i).isAlwaysOnTop())) {
belowAlwaysOnTopPosition = i;
break;
}
@@ -4158,10 +4180,6 @@
POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
}
- int targetPosition = requestedPosition;
- targetPosition = Math.min(targetPosition, maxPosition);
- targetPosition = Math.max(targetPosition, minPosition);
-
// Cap the requested position to something reasonable for the previous position check
// below.
if (requestedPosition == POSITION_TOP) {
@@ -4170,6 +4188,10 @@
requestedPosition = 0;
}
+ int targetPosition = requestedPosition;
+ targetPosition = Math.min(targetPosition, maxPosition);
+ targetPosition = Math.max(targetPosition, minPosition);
+
int prevPosition = getStacks().indexOf(stack);
// The positions we calculated above (maxPosition, minPosition) do not take into
// consideration the following edge cases.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 77e557b..2283041 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2215,9 +2215,22 @@
}
private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
+ final int rotation = displayFrames.mRotation;
+ final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
+ displayFrames.mDisplayHeight, rotation);
+
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+ if (navBarPosition == NAV_BAR_BOTTOM) {
+ // Always account for the nav bar frame height on the bottom since in all navigation
+ // modes we make room to show the dismiss-ime button, even if the IME does not report
+ // insets (ie. when floating)
+ final int uimode = mService.mPolicy.getUiMode();
+ final int navFrameHeight = getNavigationBarFrameHeight(rotation, uimode);
+ displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom,
+ displayFrames.mUnrestricted.bottom - navFrameHeight);
+ }
displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
top = win.getVisibleFrameLw().top;
top += win.getGivenVisibleInsetsLw().top;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 2da76ea..78fea74 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -63,7 +63,7 @@
mDisplayListeners.finishBroadcast();
}
- void dispatchDisplayChanged(ActivityDisplay display, Configuration newConfig) {
+ void dispatchDisplayChanged(DisplayContent display, Configuration newConfig) {
// Only report changed if this has actually been added to the hierarchy already.
boolean isInHierarchy = false;
for (int i = 0; i < display.getParent().getChildCount(); ++i) {
@@ -78,7 +78,7 @@
for (int i = 0; i < count; ++i) {
try {
mDisplayListeners.getBroadcastItem(i).onDisplayConfigurationChanged(
- display.mDisplayId, newConfig);
+ display.getDisplayId(), newConfig);
} catch (RemoteException e) {
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 7645de5..318d88e 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -531,8 +531,8 @@
* occlusion state.
*/
private ActivityStack getStackForControllingOccluding(ActivityDisplay display) {
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
if (stack != null && stack.isFocusableAndVisible()
&& !stack.inPinnedWindowingMode()) {
return stack;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f4c867c..d5bbe6b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -475,8 +475,8 @@
* @return The top stack that is not always-on-top.
*/
private ActivityStack getTopNonAlwaysOnTopStack() {
- for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
- final ActivityStack s = mDefaultDisplay.getChildAt(i);
+ for (int i = mDefaultDisplay.getStackCount() - 1; i >= 0; i--) {
+ final ActivityStack s = mDefaultDisplay.getStackAt(i);
if (s.getWindowConfiguration().isAlwaysOnTop()) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 5fd59fa..40e6dcc 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -52,7 +52,6 @@
import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
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_TASKS;
@@ -770,10 +769,12 @@
starting.frozenBeforeDestroy = true;
}
- if (displayContent != null && displayContent.mActivityDisplay != null) {
+ if (displayContent != null) {
// Update the configuration of the activities on the display.
- return displayContent.mActivityDisplay.updateDisplayOverrideConfigurationLocked(config,
- starting, deferResume, null /* result */);
+ // TODO(display-merge): Remove cast
+ return ((ActivityDisplay) displayContent)
+ .updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+ null /* result */);
} else {
return true;
}
@@ -790,8 +791,8 @@
for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
final ActivityDisplay display = mActivityDisplays.get(i);
// Traverse all stacks on a display.
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
+ for (int j = display.getStackCount() - 1; j >= 0; --j) {
+ final ActivityStack stack = display.getStackAt(j);
// Get top activity from a visible stack and add it to the list.
if (stack.shouldBeVisible(null /* starting */)) {
final ActivityRecord top = stack.getTopNonFinishingActivity();
@@ -861,8 +862,8 @@
WindowProcessController fgApp = null;
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
if (isTopDisplayFocusedStack(stack)) {
final ActivityRecord resumedActivity = stack.getResumedActivity();
if (resumedActivity != null) {
@@ -988,8 +989,8 @@
mStackSupervisor.mStartingUsers.add(uss);
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
stack.switchUser(userId);
Task task = stack.topTask();
if (task != null) {
@@ -1056,7 +1057,7 @@
+ " to its current displayId=" + displayId);
}
- if (activityDisplay.isSingleTaskInstance() && activityDisplay.getChildCount() > 0) {
+ if (activityDisplay.isSingleTaskInstance() && activityDisplay.getStackCount() > 0) {
// We don't allow moving stacks to single instance display that already has a child.
Slog.e(TAG, "Can not move stack=" + stack
+ " to single task instance display=" + activityDisplay);
@@ -1229,8 +1230,8 @@
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
// It is possible that request to finish activity might also remove its task and stack,
// so we need to be careful with indexes in the loop and check child count every time.
- for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = 0; stackNdx < display.getStackCount(); ++stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final Task t = stack.finishTopCrashedActivityLocked(app, reason);
if (stack == focusedStack || finishedTask == null) {
finishedTask = t;
@@ -1260,8 +1261,8 @@
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
boolean resumedOnDisplay = false;
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
continue;
@@ -1314,8 +1315,8 @@
}
// Set the sleeping state of the stacks on the display.
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
if (displayShouldSleep) {
stack.goToSleepIfPossible(false /* shuttingDown */);
} else {
@@ -1355,9 +1356,9 @@
}
}
- protected <T extends ActivityStack> T getStack(int stackId) {
+ protected ActivityStack getStack(int stackId) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(stackId);
+ final ActivityStack stack = mActivityDisplays.get(i).getStack(stackId);
if (stack != null) {
return stack;
}
@@ -1366,9 +1367,10 @@
}
/** @see ActivityDisplay#getStack(int, int) */
- private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+ ActivityStack getStack(int windowingMode, int activityType) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+ final ActivityStack stack =
+ mActivityDisplays.get(i).getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
@@ -1376,7 +1378,7 @@
return null;
}
- private <T extends ActivityStack> T getStack(int windowingMode, int activityType,
+ private ActivityStack getStack(int windowingMode, int activityType,
int displayId) {
ActivityDisplay display = getActivityDisplay(displayId);
if (display == null) {
@@ -1449,8 +1451,8 @@
if (displayId == INVALID_DISPLAY) {
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
list.add(getStackInfo(stack));
}
}
@@ -1460,8 +1462,8 @@
if (display == null) {
return list;
}
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
list.add(getStackInfo(stack));
}
return list;
@@ -1549,9 +1551,9 @@
ActivityStack findStackBehind(ActivityStack stack) {
final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
if (display != null) {
- for (int i = display.getChildCount() - 1; i >= 0; i--) {
- if (display.getChildAt(i) == stack && i > 0) {
- return display.getChildAt(i - 1);
+ for (int i = display.getStackCount() - 1; i >= 0; i--) {
+ if (display.getStackAt(i) == stack && i > 0) {
+ return display.getStackAt(i - 1);
}
}
}
@@ -1575,7 +1577,7 @@
}
// TODO: remove after object merge with RootWindowContainer
- void onChildPositionChanged(ActivityDisplay display, int position) {
+ void onChildPositionChanged(DisplayContent display, int position) {
// Assume AM lock is held from positionChildAt of controller in each hierarchy.
if (display != null) {
positionChildAt(display, position);
@@ -1583,18 +1585,20 @@
}
/** Change the z-order of the given display. */
- private void positionChildAt(ActivityDisplay display, int position) {
+ private void positionChildAt(DisplayContent display, int position) {
if (position >= mActivityDisplays.size()) {
position = mActivityDisplays.size() - 1;
} else if (position < 0) {
position = 0;
}
+ // TODO(display-merge): Remove cast
+ final ActivityDisplay activityDisplay = (ActivityDisplay) display;
if (mActivityDisplays.isEmpty()) {
- mActivityDisplays.add(display);
+ mActivityDisplays.add(activityDisplay);
} else if (mActivityDisplays.get(position) != display) {
mActivityDisplays.remove(display);
- mActivityDisplays.add(position, display);
+ mActivityDisplays.add(position, activityDisplay);
}
mStackSupervisor.updateTopResumedActivityIfNeeded();
}
@@ -1709,8 +1713,8 @@
void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
stack.scheduleDestroyActivities(app, reason);
}
}
@@ -1722,15 +1726,15 @@
boolean allSleep = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
// Stacks and activities could be removed while putting activities to sleep if
// the app process was gone. This prevents us getting exception by accessing an
// invalid stack index.
- if (stackNdx >= display.getChildCount()) {
+ if (stackNdx >= display.getStackCount()) {
continue;
}
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityStack stack = display.getStackAt(stackNdx);
if (allowDelay) {
allSleep &= stack.goToSleepIfPossible(shuttingDown);
} else {
@@ -1972,8 +1976,8 @@
r.getActivityType());
// Return the topmost valid stack on the display.
- for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = activityDisplay.getChildAt(i);
+ for (int i = activityDisplay.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = activityDisplay.getStackAt(i);
if (isValidLaunchStack(stack, r, windowingMode)) {
return stack;
}
@@ -2110,8 +2114,8 @@
boolean hasVisibleActivities = 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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
hasVisibleActivities |= stack.handleAppDiedLocked(app);
}
}
@@ -2223,9 +2227,9 @@
void finishVoiceTask(IVoiceInteractionSession session) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- final int numStacks = display.getChildCount();
+ final int numStacks = display.getStackCount();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ final ActivityStack stack = display.getStackAt(stackNdx);
stack.finishVoiceTask(session);
}
}
@@ -2290,8 +2294,8 @@
boolean foundResumed = 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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.getResumedActivity();
if (r != null) {
if (!r.nowVisible) {
@@ -2308,8 +2312,8 @@
boolean pausing = true;
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
if (DEBUG_STATES) {
@@ -2336,8 +2340,8 @@
try {
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final List<Task> tasks = stack.getAllTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
final Task task = tasks.get(taskNdx);
@@ -2380,8 +2384,8 @@
void cancelInitializingActivities() {
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);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
stack.cancelInitializingActivities();
}
}
@@ -2413,8 +2417,8 @@
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final Task task = stack.taskForIdLocked(id);
if (task == null) {
continue;
@@ -2471,8 +2475,8 @@
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
final ActivityRecord r = stack.isInStackLocked(token);
if (r != null) {
return r;
@@ -2554,8 +2558,8 @@
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
activities.addAll(stack.getDumpActivitiesLocked(name));
}
@@ -2606,8 +2610,8 @@
pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
pw.println(" (activities from top to bottom):");
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
pw.println();
printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
needSep = printed;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5ec9599..5a104289 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,7 +37,6 @@
import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -71,7 +70,6 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
-import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -204,6 +202,12 @@
}
@Override
+ boolean isOnTop() {
+ // Considered always on top
+ return true;
+ }
+
+ @Override
void onChildPositionChanged(WindowContainer child) {
mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
!mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
@@ -219,36 +223,6 @@
return null;
}
- DisplayContent createDisplayContent(final Display display, ActivityDisplay activityDisplay) {
- final int displayId = display.getDisplayId();
-
- // In select scenarios, it is possible that a DisplayContent will be created on demand
- // rather than waiting for the controller. In this case, associate the controller and return
- // the existing display.
- final DisplayContent existing = getDisplayContent(displayId);
-
- if (existing != null) {
- existing.mActivityDisplay = activityDisplay;
- existing.initializeDisplayOverrideConfiguration();
- return existing;
- }
-
- final DisplayContent dc = new DisplayContent(display, mWmService, activityDisplay);
-
- if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
-
- mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
- dc.initializeDisplayOverrideConfiguration();
-
- if (mWmService.mDisplayManagerInternal != null) {
- mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
- displayId, dc.getDisplayInfo());
- dc.configureDisplayPolicy();
- }
-
- return dc;
- }
-
/**
* Called when DisplayWindowSettings values may change.
*/
@@ -262,7 +236,6 @@
continue;
}
- displayContent.initializeDisplayOverrideConfiguration();
displayContent.reconfigureDisplayLocked();
// We need to update global configuration as well if config of default display has
@@ -1039,7 +1012,7 @@
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final DisplayContent displayContent = mChildren.get(i);
- displayContent.writeToProto(proto, DISPLAYS, logLevel);
+ displayContent.writeToProtoInner(proto, DISPLAYS, logLevel);
}
}
if (logLevel == WindowTraceLogLevel.ALL) {
@@ -1059,7 +1032,7 @@
void positionChildAt(int position, DisplayContent child, boolean includingParents) {
super.positionChildAt(position, child, includingParents);
if (mRootActivityContainer != null) {
- mRootActivityContainer.onChildPositionChanged(child.mActivityDisplay, position);
+ mRootActivityContainer.onChildPositionChanged(child, position);
}
}
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index f2678bb..ca9d91e 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -52,8 +52,8 @@
final int numDisplays = activityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
final ActivityDisplay display = activityDisplays.get(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
+ for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getStackAt(stackNdx);
mTmpStackTasks.clear();
stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
callingUid, allowed, crossUser, profileIds);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 88a38e0..6e8d0b7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -968,6 +968,11 @@
final ActivityStack oldStack = ((ActivityStack) oldParent);
final ActivityStack newStack = ((ActivityStack) newParent);
+ // Task is going to be removed, clean it up before detaching from hierarchy.
+ if (oldParent != null && newParent == null) {
+ cleanUpResourcesForDestroy();
+ }
+
mStack = newStack;
super.onParentChanged(newParent, oldParent);
@@ -1012,12 +1017,6 @@
updateOverrideConfigurationFromLaunchBounds();
}
- // Task is being removed.
- if (oldParent != null && newParent == null) {
- cleanUpResourcesForDestroy();
- }
-
-
// Update task bounds if needed.
adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 8ad8972..9d19cfe 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -705,8 +705,8 @@
private void adjustBoundsToAvoidConflictInDisplay(@NonNull ActivityDisplay display,
@NonNull Rect inOutBounds) {
final List<Rect> taskBoundsToCheck = new ArrayList<>();
- for (int i = 0; i < display.getChildCount(); ++i) {
- final ActivityStack stack = display.getChildAt(i);
+ for (int i = 0; i < display.getStackCount(); ++i) {
+ final ActivityStack stack = display.getStackAt(i);
if (!stack.inFreeformWindowingMode()) {
continue;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5c1491e..1b4ca41 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -288,10 +288,8 @@
final DisplayContent dc = newParent.getDisplayContent();
mReparenting = true;
- // Oddly enough we add to the new parent before removing from the old parent to avoid
- // issues...
- newParent.addChild(this, position);
oldParent.removeChild(this);
+ newParent.addChild(this, position);
mReparenting = false;
// Relayout display(s)
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 46faf3b..ea90e49 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -550,4 +550,10 @@
* the next time the activities are opened.
*/
public abstract void clearSnapshotCache();
+
+ /**
+ * Assigns accessibility ID a window surface as a layer metadata.
+ */
+ public abstract void setAccessibilityIdToSurfaceMetadata(
+ IBinder windowToken, int accessibilityWindowId);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4ec50f0..51221dd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1732,20 +1732,7 @@
}
}
- DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-
- // Create an instance if possible instead of waiting for the ActivityManagerService to drive
- // the creation.
- if (displayContent == null) {
- final Display display = mDisplayManager.getDisplay(displayId);
-
- if (display != null) {
- displayContent = mRoot.createDisplayContent(display, null /* activityDisplay */);
- displayContent.reconfigureDisplayLocked();
- }
- }
-
- return displayContent;
+ return mAtmService.mRootActivityContainer.getActivityDisplayOrCreate(displayId);
}
private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
@@ -2350,9 +2337,8 @@
win.setLastReportedMergedConfiguration(mergedConfiguration);
- // Update the last frames and inset values here because the values are sent back to the
- // client. The last values represent the last client state.
- win.updateLastFrames();
+ // Update the last inset values here because the values are sent back to the client.
+ // The last inset values represent the last client state
win.updateLastInsetValues();
win.getCompatFrame(outFrame);
@@ -7412,6 +7398,27 @@
public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) {
return mKeyInterceptionInfoForToken.get(inputToken);
}
+
+ @Override
+ public void setAccessibilityIdToSurfaceMetadata(
+ IBinder windowToken, int accessibilityWindowId) {
+ synchronized (mGlobalLock) {
+ final WindowState state = mWindowMap.get(windowToken);
+ if (state == null) {
+ Slog.w(TAG, "Cannot find window which accessibility connection is added to");
+ return;
+ }
+ try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+ t.setMetadata(
+ state.mSurfaceControl,
+ SurfaceControl.METADATA_ACCESSIBILITY_ID,
+ accessibilityWindowId);
+ t.apply();
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
@@ -7626,12 +7633,15 @@
// it as if the host window was tapped.
touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
}
- if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
+
+ if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) {
+ // If the window that received the input event cannot receive keys, don't move the
+ // display it's on to the top since that window won't be able to get focus anyway.
return;
}
- handleTaskFocusChange(touchedWindow.getTask());
handleDisplayFocusChange(touchedWindow);
+ handleTaskFocusChange(touchedWindow.getTask());
}
private void handleTaskFocusChange(Task task) {
@@ -7659,12 +7669,6 @@
return;
}
- if (!window.canReceiveKeys()) {
- // If the window that received the input event cannot receive keys, don't move the
- // display it's on to the top since that window won't be able to get focus anyway.
- return;
- }
-
final WindowContainer parent = displayContent.getParent();
if (parent != null && parent.getTopChild() != displayContent) {
parent.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
@@ -7676,7 +7680,8 @@
// to do so because it seems possible to resume activities as part of a larger
// transaction and it's too early to resume based on current order when performing
// updateTopResumedActivityIfNeeded().
- displayContent.mActivityDisplay.ensureActivitiesVisible(null /* starting */,
+ // TODO(display-merge): Remove cast
+ ((ActivityDisplay) displayContent).ensureActivitiesVisible(null /* starting */,
0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ce26e5f..dcf4b38 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -35,6 +35,7 @@
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -2162,12 +2163,13 @@
return false;
}
- // Can be an IME target only if:
- // 1. FLAG_NOT_FOCUSABLE is not set
- // 2. FLAG_ALT_FOCUSABLE_IM is not set
- // 3. not a starting window.
- if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags)
- || mAttrs.type == TYPE_APPLICATION_STARTING) {
+ final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+ final int type = mAttrs.type;
+
+ // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or
+ // both are cleared...and not a starting window.
+ if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
+ && type != TYPE_APPLICATION_STARTING) {
return false;
}
@@ -2616,11 +2618,22 @@
@Override
public boolean canReceiveKeys() {
- return isVisibleOrAdding()
+ return canReceiveKeys(false /* fromUserTouch */);
+ }
+
+ public boolean canReceiveKeys(boolean fromUserTouch) {
+ final boolean canReceiveKeys = isVisibleOrAdding()
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable())
&& !cantReceiveTouchInput();
+ if (!canReceiveKeys) {
+ return false;
+ }
+ // Do not allow untrusted virtual display to receive keys unless user intentionally
+ // touches the display.
+ return fromUserTouch || getDisplayContent().isOnTop()
+ || !getDisplayContent().isUntrustedVirtualDisplay();
}
@Override
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 612a1e7..dcff5a1 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -338,7 +338,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
int32_t lengthMs;
sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
- aidl::Effect effectType(static_cast<aidl::Effect>(strength));
+ aidl::Effect effectType(static_cast<aidl::Effect>(effect));
aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));
auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs);
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index ee39e50..a70568f 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -28,6 +28,7 @@
<xs:attribute type="xs:string" name="name" use="required"/>
<xs:attribute type="xs:boolean" name="disabled"/>
<xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+ <xs:attribute type="xs:string" name="description"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index 8456785..3a33f63 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -3,11 +3,13 @@
public class Change {
ctor public Change();
+ method public String getDescription();
method public boolean getDisabled();
method public int getEnableAfterTargetSdk();
method public long getId();
method public String getName();
method public String getValue();
+ method public void setDescription(String);
method public void setDisabled(boolean);
method public void setEnableAfterTargetSdk(int);
method public void setId(long);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2499ad8..cb599be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -139,6 +139,8 @@
import android.app.backup.IBackupManager;
import android.app.timedetector.ManualTimeSuggestion;
import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
@@ -1962,6 +1964,10 @@
return mContext.getSystemService(TimeDetector.class);
}
+ TimeZoneDetector getTimeZoneDetector() {
+ return mContext.getSystemService(TimeZoneDetector.class);
+ }
+
ConnectivityManager getConnectivityManager() {
return mContext.getSystemService(ConnectivityManager.class);
}
@@ -2748,6 +2754,9 @@
return null;
}
+ // Code for handling failure from getActiveAdminWithPolicyForUidLocked to find an admin
+ // that satisfies the required policy.
+ // Throws a security exception with the right error message.
if (who != null) {
final int userId = UserHandle.getUserId(callingUid);
final DevicePolicyData policy = getUserData(userId);
@@ -2763,6 +2772,10 @@
throw new SecurityException("Admin " + admin.info.getComponent()
+ " does not own the profile");
}
+ if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) {
+ throw new SecurityException("Admin " + admin.info.getComponent()
+ + " is not the profile owner on organization-owned device");
+ }
if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) {
throw new SecurityException("Admin " + admin.info.getComponent()
+ " is not a device owner or profile owner, so may not use policy: "
@@ -2869,12 +2882,16 @@
ensureLocked();
final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId);
final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId);
+ final boolean ownsProfileOnOrganizationOwnedDevice =
+ isProfileOwnerOfOrganizationOwnedDevice(admin.info.getComponent(), userId);
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
return ownsDevice;
+ } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) {
+ return ownsDevice || ownsProfileOnOrganizationOwnedDevice;
} else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
// DO always has the PO power.
- return ownsDevice || ownsProfile;
+ return ownsDevice || ownsProfileOnOrganizationOwnedDevice || ownsProfile;
} else {
boolean allowedToUsePolicy = ownsDevice || ownsProfile
|| !DA_DISALLOWED_POLICIES.contains(reqPolicy)
@@ -5574,6 +5591,13 @@
}
}
+ private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) {
+ synchronized (getLockObject()) {
+ getActiveAdminForCallerLocked(
+ who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+ }
+ }
+
private void enforceProfileOwnerOfOrganizationOwnedDevice(ActiveAdmin admin) {
if (!isProfileOwnerOfOrganizationOwnedDevice(admin)) {
throw new SecurityException(String.format("Provided admin %s is either not a profile "
@@ -8071,21 +8095,12 @@
return false;
}
- final int adminUserId = admin.getUserHandle().getIdentifier();
+ return isProfileOwnerOfOrganizationOwnedDevice(
+ admin.info.getComponent(), admin.getUserHandle().getIdentifier());
+ }
- if (!isProfileOwner(admin.info.getComponent(), adminUserId)) {
- Slog.w(LOG_TAG, String.format("%s is not profile owner of user %d",
- admin.info.getComponent(), adminUserId));
- return false;
- }
-
- if (!canProfileOwnerAccessDeviceIds(adminUserId)) {
- Slog.w(LOG_TAG, String.format("Profile owner of user %d does not own the device.",
- adminUserId));
- return false;
- }
-
- return true;
+ private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) {
+ return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId);
}
@Override
@@ -11123,8 +11138,11 @@
if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
return false;
}
+ ManualTimeZoneSuggestion manualTimeZoneSuggestion =
+ TimeZoneDetector.createManualTimeZoneSuggestion(
+ timeZone, "DevicePolicyManagerService: setTimeZone");
mInjector.binderWithCleanCallingIdentity(() ->
- mInjector.getAlarmManager().setTimeZone(timeZone));
+ mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion));
return true;
}
@@ -12377,7 +12395,7 @@
@Override
public String getWifiMacAddress(ComponentName admin) {
// Make sure caller has DO.
- enforceDeviceOwner(admin);
+ enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
final long ident = mInjector.binderClearCallingIdentity();
try {
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index db01ae4..61cd88a 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -17,8 +17,11 @@
package android.net.ip;
import android.net.DhcpResults;
+import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
+import java.util.List;
+
/**
* Callbacks for handling IpClient events.
*
@@ -116,4 +119,9 @@
* whenever 464xlat is being started or stopped.
*/
public void setNeighborDiscoveryOffload(boolean enable) {}
+
+ /**
+ * Invoked on starting preconnection process.
+ */
+ public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {}
}
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 1e653cd..4b7ed3c 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -234,7 +234,7 @@
slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
return true;
} catch (RemoteException e) {
- log("Error adding Keepalive Packet Filter ", e);
+ log("Error adding NAT-T Keepalive Packet Filter ", e);
return false;
} finally {
Binder.restoreCallingIdentity(token);
@@ -272,4 +272,22 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ /**
+ * Notify IpClient that preconnection is complete and that the link is ready for use.
+ * The success parameter indicates whether the packets passed in by 'onPreconnectionStart'
+ * were successfully sent to the network or not.
+ */
+ public boolean notifyPreconnectionComplete(boolean success) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mIpClient.notifyPreconnectionComplete(success);
+ return true;
+ } catch (RemoteException e) {
+ log("Error notifying IpClient Preconnection completed", e);
+ return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 714ade1..4d60e62 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -20,12 +20,14 @@
import android.content.Context;
import android.net.DhcpResultsParcelable;
+import android.net.Layer2PacketParcelable;
import android.net.LinkProperties;
import android.net.NetworkStackClient;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/**
@@ -176,6 +178,12 @@
mCb.setNeighborDiscoveryOffload(enable);
}
+ // Invoked on starting preconnection process.
+ @Override
+ public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
+ mCb.onPreconnectionStart(packets);
+ }
+
@Override
public int getInterfaceVersion() {
return this.VERSION;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 6c09239..8e6114a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1053,6 +1053,35 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_2() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ bindService(app, client, null, 0, mock(IBinder.class));
+ bindService(client, app, null, 0, mock(IBinder.class));
+ ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ bindService(client2, client, null, 0, mock(IBinder.class));
+ client.setHasForegroundServices(true, 0);
+ ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+ lru.clear();
+ lru.add(app);
+ lru.add(client);
+ lru.add(client2);
+ sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+ sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(client, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ assertProcStates(client2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_Branch() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index fb42507..3a07a69 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -40,6 +40,7 @@
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 37f5b87..3352177 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -16,18 +16,39 @@
package com.android.server.accessibility;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
import android.os.Handler;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import org.junit.Before;
@@ -35,55 +56,290 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Tests for SystemActionPerformer
*/
public class SystemActionPerformerTest {
- SystemActionPerformer mSystemActionPerformer;
+ private static final int LATCH_TIMEOUT_MS = 500;
+ private static final int LEGACY_SYSTEM_ACTION_COUNT = 9;
+ private static final int NEW_ACTION_ID = 20;
+ private static final String LABEL_1 = "label1";
+ private static final String LABEL_2 = "label2";
+ private static final String INTENT_ACTION1 = "TESTACTION1";
+ private static final String INTENT_ACTION2 = "TESTACTION2";
+ private static final String DESCRIPTION1 = "description1";
+ private static final String DESCRIPTION2 = "description2";
+ private static final PendingIntent TEST_PENDING_INTENT_1 = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION1), 0);
+ private static final RemoteAction NEW_TEST_ACTION_1 = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL_1,
+ DESCRIPTION1,
+ TEST_PENDING_INTENT_1);
+ private static final PendingIntent TEST_PENDING_INTENT_2 = PendingIntent.getBroadcast(
+ InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION2), 0);
+ private static final RemoteAction NEW_TEST_ACTION_2 = new RemoteAction(
+ Icon.createWithContentUri("content://test"),
+ LABEL_2,
+ DESCRIPTION2,
+ TEST_PENDING_INTENT_2);
- @Mock Context mMockContext;
- @Mock WindowManagerInternal mMockWindowManagerInternal;
- @Mock StatusBarManager mMockStatusBarManager;
- @Mock ScreenshotHelper mMockScreenshotHelper;
+ private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(NEW_ACTION_ID, LABEL_1);
+ private static final AccessibilityAction LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, LABEL_1);
+ private static final AccessibilityAction LEGACY_HOME_ACCESSIBILITY_ACTION =
+ new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_HOME, LABEL_2);
+
+ private SystemActionPerformer mSystemActionPerformer;
+
+ @Mock private Context mMockContext;
+ @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal;
+ @Mock private WindowManagerInternal mMockWindowManagerInternal;
+ @Mock private StatusBarManager mMockStatusBarManager;
+ @Mock private ScreenshotHelper mMockScreenshotHelper;
+ @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+ }
- when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
- .thenReturn(mMockStatusBarManager);
+ private void setupWithMockContext() {
+ doReturn(mMockStatusBarManager).when(
+ mMockContext).getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+ doReturn(InstrumentationRegistry.getContext().getResources()).when(
+ mMockContext).getResources();
+ mSystemActionPerformer = new SystemActionPerformer(
+ mMockContext,
+ mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper,
+ mMockListener);
+ }
- mSystemActionPerformer =
- new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
- () -> mMockScreenshotHelper);
+ private void setupWithRealContext() {
+ mSystemActionPerformer = new SystemActionPerformer(
+ InstrumentationRegistry.getContext(),
+ mMockWindowManagerInternal,
+ () -> mMockScreenshotHelper,
+ mMockListener);
+ }
+
+ // We need below two help functions because AccessbilityAction.equals function only compares
+ // action ids. To verify the test result here, we are also looking at action labels.
+ private void assertHasLegacyAccessibilityAction(
+ List<AccessibilityAction> actions, AccessibilityAction action) {
+ boolean foundAction = false;
+ for (AccessibilityAction a : actions) {
+ if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+ foundAction = true;
+ break;
+ }
+ }
+ assertTrue(foundAction);
+ }
+
+ private void assertHasNoLegacyAccessibilityAction(
+ List<AccessibilityAction> actions, AccessibilityAction action) {
+ boolean foundAction = false;
+ for (AccessibilityAction a : actions) {
+ if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+ foundAction = true;
+ break;
+ }
+ }
+ assertFalse(foundAction);
}
@Test
- public void testNotifications_expandsNotificationPanel() {
+ public void testRegisterSystemAction_addedIntoAvailableSystemActions() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Register a new system action
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+ assertThat(actions, hasItem(NEW_ACCESSIBILITY_ACTION));
+ }
+
+ @Test
+ public void testRegisterSystemAction_overrideLegacyAction() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Overriding a legacy system action using legacy notification action id
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+ }
+
+ @Test
+ public void testUnregisterSystemAction_removeFromAvailableSystemActions() {
+ setupWithRealContext();
+ // Before any new system action is registered, getSystemActions returns all legacy actions
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ // Register a new system action
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+
+ mSystemActionPerformer.unregisterSystemAction(NEW_ACTION_ID);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertThat(actions, is(not(hasItem(NEW_ACCESSIBILITY_ACTION))));
+ }
+
+ @Test
+ public void testUnregisterSystemAction_removeOverrideForLegacyAction() {
+ setupWithRealContext();
+
+ // Overriding a legacy system action
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+ List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+
+ // Remove the overriding action using legacy action id
+ mSystemActionPerformer.unregisterSystemAction(
+ AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+ actions = mSystemActionPerformer.getSystemActions();
+ assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+ assertHasNoLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+ }
+
+ @Test
+ public void testPerformSystemActionNewAction() throws CanceledException {
+ setupWithRealContext();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+ TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+ br.register(InstrumentationRegistry.getTargetContext());
+ mSystemActionPerformer.performSystemAction(NEW_ACTION_ID);
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("RemoteAction should be triggered.");
+ } finally {
+ br.unregister(InstrumentationRegistry.getTargetContext());
+ }
+ }
+
+ @Test
+ public void testPerformSystemActionOverrideLegacyActionUsingLegacyActionId()
+ throws CanceledException {
+ setupWithRealContext();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ mSystemActionPerformer.registerSystemAction(
+ AccessibilityService.GLOBAL_ACTION_RECENTS, NEW_TEST_ACTION_1);
+ TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+ br.register(InstrumentationRegistry.getTargetContext());
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("RemoteAction should be triggered.");
+ } finally {
+ br.unregister(InstrumentationRegistry.getTargetContext());
+ }
+ verify(mMockStatusBarManagerInternal, never()).toggleRecentApps();
+ // Now revert to legacy action
+ mSystemActionPerformer.unregisterSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ verify(mMockStatusBarManagerInternal).toggleRecentApps();
+ }
+
+ @Test
+ public void testNotifications_expandsNotificationPanel_legacy() {
+ setupWithMockContext();
mSystemActionPerformer
.performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
verify(mMockStatusBarManager).expandNotificationsPanel();
}
@Test
- public void testQuickSettings_requestsQuickSettingsPanel() {
+ public void testQuickSettings_requestsQuickSettingsPanel_legacy() {
+ setupWithMockContext();
mSystemActionPerformer
.performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
verify(mMockStatusBarManager).expandSettingsPanel();
}
@Test
- public void testPowerDialog_requestsFromWindowManager() {
+ public void testRecentApps_legacy() {
+ setupWithRealContext();
+ mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+ verify(mMockStatusBarManagerInternal).toggleRecentApps();
+ }
+
+ @Test
+ public void testPowerDialog_requestsFromWindowManager_legacy() {
+ setupWithMockContext();
mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
verify(mMockWindowManagerInternal).showGlobalActions();
}
@Test
- public void testScreenshot_requestsFromScreenshotHelper() {
+ public void testToggleSplitScreen_legacy() {
+ setupWithRealContext();
+ mSystemActionPerformer.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+ verify(mMockStatusBarManagerInternal).toggleSplitScreen();
+ }
+
+ @Test
+ public void testScreenshot_requestsFromScreenshotHelper_legacy() {
+ setupWithMockContext();
mSystemActionPerformer.performSystemAction(
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
verify(mMockScreenshotHelper).takeScreenshot(
eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
anyBoolean(), any(Handler.class), any());
}
+
+ // PendingIntent is a final class and cannot be mocked. So we are using this
+ // Broadcast receiver to verify the registered remote action is called correctly.
+ private static final class TestBroadcastReceiver extends BroadcastReceiver {
+ private CountDownLatch mLatch;
+ private boolean mRegistered;
+ private final IntentFilter mFilter;
+
+ TestBroadcastReceiver(CountDownLatch latch) {
+ mLatch = latch;
+ mRegistered = false;
+ mFilter = new IntentFilter(INTENT_ACTION1);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLatch.countDown();
+ }
+
+ void register(Context context) {
+ if (!mRegistered) {
+ context.registerReceiver(this, mFilter);
+ mRegistered = true;
+ }
+ }
+
+ void unregister(Context context) {
+ if (mRegistered) {
+ context.unregisterReceiver(this);
+ }
+ }
+ }
}
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
index adef02e..6f2de7f 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -21,6 +21,7 @@
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SearchResultProto;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -115,8 +116,9 @@
private static List<String> queryGetUris(FakeIcing icing, String term) {
List<String> uris = new ArrayList<>();
- for (DocumentProto result : icing.query(term)) {
- uris.add(result.getUri());
+ SearchResultProto results = icing.query(term);
+ for (SearchResultProto.ResultProto result : results.getResultsList()) {
+ uris.add(result.getDocument().getUri());
}
return uris;
}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index f8c87fc..7267976 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -73,36 +73,36 @@
@Test
public void testDisabledChangeDisabled() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, ""));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
}
@Test
public void testTargetSdkChangeDisabled() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
}
@Test
public void testTargetSdkChangeEnabled() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, ""));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
}
@Test
public void testDisabledOverrideTargetSdkChange() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse();
}
@Test
public void testGetDisabledChanges() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true));
- pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null));
+ pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null));
assertThat(pc.getDisabledChanges(
makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L);
}
@@ -110,9 +110,9 @@
@Test
public void testGetDisabledChangesSorted() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true));
- pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true));
- pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
+ pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null));
+ pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null));
assertThat(pc.getDisabledChanges(
makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L);
}
@@ -120,7 +120,7 @@
@Test
public void testPackageOverrideEnabled() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true)); // disabled
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled
pc.addOverride(1234L, "com.some.package", true);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse();
@@ -129,7 +129,7 @@
@Test
public void testPackageOverrideDisabled() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
pc.addOverride(1234L, "com.some.package", false);
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
@@ -152,7 +152,7 @@
@Test
public void testRemovePackageOverride() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
pc.addOverride(1234L, "com.some.package", false);
pc.removeOverride(1234L, "com.some.package");
assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
@@ -161,8 +161,8 @@
@Test
public void testLookupChangeId() {
CompatConfig pc = new CompatConfig();
- pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
- pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false));
+ pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
+ pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null));
assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
}
@@ -174,8 +174,9 @@
@Test
public void testReadConfig() {
- Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
- "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+ Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2, null), new Change(1235L,
+ "MY_CHANGE2", true, null, "description"), new Change(1236L, "MY_CHANGE3", false,
+ null, "")};
File dir = createTempDir();
writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
@@ -191,9 +192,9 @@
@Test
public void testReadConfigMultipleFiles() {
- Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
- Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
- "MY_CHANGE3", false, null)};
+ Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2, null)};
+ Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null, ""), new Change(1236L,
+ "MY_CHANGE3", false, null, null)};
File dir = createTempDir();
writeChangesToFile(changes1,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index f86bacf..ac555fd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -23,6 +23,7 @@
import android.app.PendingIntent;
import android.app.backup.IBackupManager;
import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.content.Intent;
@@ -235,6 +236,11 @@
}
@Override
+ TimeZoneDetector getTimeZoneDetector() {
+ return services.timeZoneDetector;
+ }
+
+ @Override
LockPatternUtils newLockPatternUtils() {
return services.lockPatternUtils;
}
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 0590020..06b8716 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -64,6 +64,8 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.PasswordMetrics;
import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -138,6 +140,8 @@
permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
public static final String NOT_DEVICE_OWNER_MSG = "does not own the device";
public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile";
+ public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG =
+ "not the profile owner on organization-owned device";
public static final String ONGOING_CALL_MSG = "ongoing call on the device";
// TODO replace all instances of this with explicit {@link #mServiceContext}.
@@ -2114,12 +2118,14 @@
assertTrue(dpm.isAdminActive(admin1));
// Test 2. Caller has DA, but not DO.
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+ assertExpectException(SecurityException.class,
+ /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG,
() -> dpm.getWifiMacAddress(admin1));
// Test 3. Caller has PO, but not DO.
assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
- assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+ assertExpectException(SecurityException.class,
+ /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG,
() -> dpm.getWifiMacAddress(admin1));
// Remove PO.
@@ -2141,6 +2147,15 @@
assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
}
+ public void testGetMacAddressByOrgOwnedPO() throws Exception {
+ setupProfileOwner();
+ configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+ final String[] macAddresses = new String[]{"11:22:33:44:55:66"};
+ when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses);
+ assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
+ }
+
public void testReboot() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -3694,7 +3709,9 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
dpm.setTimeZone(admin1, "Asia/Shanghai");
- verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+ ManualTimeZoneSuggestion suggestion =
+ TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
+ verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
}
public void testSetTimeZoneFailWithPO() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index c927364..6a0d926 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -32,6 +32,7 @@
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.TimeZoneDetector;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
@@ -113,6 +114,7 @@
public final AccountManager accountManager;
public final AlarmManager alarmManager;
public final TimeDetector timeDetector;
+ public final TimeZoneDetector timeZoneDetector;
public final KeyChain.KeyChainConnection keyChainConnection;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -155,6 +157,7 @@
accountManager = mock(AccountManager.class);
alarmManager = mock(AlarmManager.class);
timeDetector = mock(TimeDetector.class);
+ timeZoneDetector = mock(TimeZoneDetector.class);
keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
// Package manager is huge, so we use a partial mock instead.
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
new file mode 100644
index 0000000..88b6d70
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -0,0 +1,628 @@
+/*
+ * 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.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Rule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleBinaryParserTest {
+
+ private static final String COMPOUND_FORMULA_START_BITS =
+ getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
+ private static final String COMPOUND_FORMULA_END_BITS =
+ getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
+ private static final String ATOMIC_FORMULA_START_BITS =
+ getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
+ private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3;
+ private static final String INVALID_FORMULA_SEPARATOR_BITS =
+ getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
+
+ private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
+ private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
+ private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
+ private static final int INVALID_CONNECTOR_VALUE = 3;
+ private static final String INVALID_CONNECTOR =
+ getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS);
+
+ private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
+ private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
+ private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
+ private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
+ private static final int INVALID_KEY_VALUE = 6;
+ private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
+
+ private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
+ private static final int INVALID_OPERATOR_VALUE = 5;
+ private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS);
+
+ private static final String IS_NOT_HASHED = "0";
+
+ private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
+ private static final int INVALID_EFFECT_VALUE = 5;
+ private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS);
+
+ private static final String START_BIT = "1";
+ private static final String END_BIT = "1";
+ private static final String INVALID_MARKER_BIT = "0";
+
+ private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
+ getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+
+ @Test
+ public void testBinaryStream_validCompoundFormula() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ InputStream inputStream = new ByteArrayInputStream(rule.array());
+ Rule expectedRule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(inputStream);
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validCompoundFormula_notConnector() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validCompoundFormula_andConnector() throws Exception {
+ String packageName = "com.test.app";
+ String appCertificate = "test_cert";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + AND
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validCompoundFormula_orConnector() throws Exception {
+ String packageName = "com.test.app";
+ String appCertificate = "test_cert";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + OR
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.OR,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validAtomicFormula_stringValue() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validAtomicFormula_integerValue() throws Exception {
+ String versionCode = "1";
+ String ruleBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + VERSION_CODE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(versionCode.length(), VALUE_SIZE_BITS)
+ + getValueBits(versionCode)
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new AtomicFormula.IntAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_validAtomicFormula_booleanValue() throws Exception {
+ String isPreInstalled = "1";
+ String ruleBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + PRE_INSTALLED
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(isPreInstalled.length(), VALUE_SIZE_BITS)
+ + getValueBits(isPreInstalled)
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+ Rule expectedRule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+ }
+
+ @Test
+ public void testBinaryString_invalidAtomicFormula() throws Exception {
+ String versionCode = "test";
+ String ruleBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + VERSION_CODE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(versionCode.length(), VALUE_SIZE_BITS)
+ + getValueBits(versionCode)
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ "For input string:",
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_withNoRuleList() throws RuleParseException {
+ ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ List<Rule> rules = binaryParser.parse(rule.array());
+
+ assertThat(rules).isEmpty();
+ }
+
+ @Test
+ public void testBinaryString_withEmptyRule() throws RuleParseException {
+ String ruleBits = START_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ "Invalid byte index",
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception {
+ String packageName = "com.test.app";
+ String appCertificate = "test_cert";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidOperator() throws Exception {
+ String versionCode = "1";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + VERSION_CODE
+ + INVALID_OPERATOR
+ + IS_NOT_HASHED
+ + getBits(versionCode.length(), VALUE_SIZE_BITS)
+ + getValueBits(versionCode)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ String.format(
+ "Unknown operator: %d", INVALID_OPERATOR_VALUE),
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidEffect() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + INVALID_EFFECT
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ String.format(
+ "Unknown effect: %d", INVALID_EFFECT_VALUE),
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidConnector() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + INVALID_CONNECTOR
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ String.format(
+ "Unknown connector: %d", INVALID_CONNECTOR_VALUE),
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidKey() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + INVALID_KEY
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ String.format(
+ "Unknown key: %d", INVALID_KEY_VALUE),
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidSeparator() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + INVALID_FORMULA_SEPARATOR_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ String.format(
+ "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE),
+ () -> binaryParser.parse(rule.array()));
+ }
+
+ @Test
+ public void testBinaryString_invalidRule_invalidEndMarker() throws Exception {
+ String packageName = "com.test.app";
+ String ruleBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + INVALID_MARKER_BIT;
+ byte[] ruleBytes = getBytes(ruleBits);
+ ByteBuffer rule =
+ ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+ rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+ rule.put(ruleBytes);
+ RuleParser binaryParser = new RuleBinaryParser();
+
+ assertExpectException(
+ RuleParseException.class,
+ /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
+ () -> binaryParser.parse(rule.array()));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
new file mode 100644
index 0000000..901277d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -0,0 +1,444 @@
+/*
+ * 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.integrity.serializer;
+
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
+import android.content.integrity.Rule;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+@RunWith(JUnit4.class)
+public class RuleBinarySerializerTest {
+
+ private static final String COMPOUND_FORMULA_START_BITS =
+ getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
+ private static final String COMPOUND_FORMULA_END_BITS =
+ getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
+ private static final String ATOMIC_FORMULA_START_BITS =
+ getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
+
+ private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
+ private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
+ private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
+
+ private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
+ private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
+ private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
+ private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
+
+ private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
+
+ private static final String IS_NOT_HASHED = "0";
+
+ private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
+
+ private static final String START_BIT = "1";
+ private static final String END_BIT = "1";
+
+ private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
+ getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+
+ @Test
+ public void testBinaryString_serializeEmptyRule() throws Exception {
+ Rule rule = null;
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ /* expectedExceptionMessageRegex= */ "Null rule can not be serialized",
+ () ->
+ binarySerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty()));
+ }
+
+ @Test
+ public void testBinaryStream_serializeValidCompoundFormula() throws Exception {
+ String packageName = "com.test.app";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ String expectedBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ binarySerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty(),
+ outputStream);
+
+ byte[] actualRules = outputStream.toByteArray();
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidCompoundFormula_notConnector() throws Exception {
+ String packageName = "com.test.app";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.NOT,
+ Collections.singletonList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + NOT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidCompoundFormula_andConnector() throws Exception {
+ String packageName = "com.test.app";
+ String appCertificate = "test_cert";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.AND,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + AND
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidCompoundFormula_orConnector() throws Exception {
+ String packageName = "com.test.app";
+ String appCertificate = "test_cert";
+ Rule rule =
+ new Rule(
+ new CompoundFormula(
+ CompoundFormula.OR,
+ Arrays.asList(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.APP_CERTIFICATE,
+ appCertificate,
+ /* isHashedValue= */ false))),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + COMPOUND_FORMULA_START_BITS
+ + OR
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + ATOMIC_FORMULA_START_BITS
+ + APP_CERTIFICATE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+ + getValueBits(appCertificate)
+ + COMPOUND_FORMULA_END_BITS
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidAtomicFormula_stringValue() throws Exception {
+ String packageName = "com.test.app";
+ Rule rule =
+ new Rule(
+ new AtomicFormula.StringAtomicFormula(
+ AtomicFormula.PACKAGE_NAME,
+ packageName,
+ /* isHashedValue= */ false),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + PACKAGE_NAME
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(packageName.length(), VALUE_SIZE_BITS)
+ + getValueBits(packageName)
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception {
+ String versionCode = "1";
+ Rule rule =
+ new Rule(
+ new AtomicFormula.IntAtomicFormula(
+ AtomicFormula.VERSION_CODE,
+ AtomicFormula.EQ,
+ Integer.parseInt(versionCode)),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + VERSION_CODE
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(versionCode.length(), VALUE_SIZE_BITS)
+ + getValueBits(versionCode)
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeValidAtomicFormula_booleanValue() throws Exception {
+ String preInstalled = "1";
+ Rule rule =
+ new Rule(
+ new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+ Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits =
+ START_BIT
+ + ATOMIC_FORMULA_START_BITS
+ + PRE_INSTALLED
+ + EQ
+ + IS_NOT_HASHED
+ + getBits(preInstalled.length(), VALUE_SIZE_BITS)
+ + getValueBits(preInstalled)
+ + DENY
+ + END_BIT;
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+ byteArrayOutputStream.write(getBytes(expectedBits));
+ byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ @Test
+ public void testBinaryString_serializeInvalidFormulaType() throws Exception {
+ Formula invalidFormula = getInvalidFormula();
+ Rule rule = new Rule(invalidFormula, Rule.DENY);
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+ assertExpectException(
+ RuleSerializeException.class,
+ /* expectedExceptionMessageRegex= */ "Invalid formula type",
+ () ->
+ binarySerializer.serialize(
+ Collections.singletonList(rule),
+ /* formatVersion= */ Optional.empty()));
+ }
+
+ @Test
+ public void testBinaryString_serializeFormatVersion() throws Exception {
+ int formatVersion = 1;
+ RuleSerializer binarySerializer = new RuleBinarySerializer();
+ String expectedBits = getBits(formatVersion, FORMAT_VERSION_BITS);
+ byte[] expectedRules = getBytes(expectedBits);
+
+ byte[] actualRules =
+ binarySerializer.serialize(
+ Collections.emptyList(), /* formatVersion= */ Optional.of(formatVersion));
+
+ assertThat(actualRules).isEqualTo(expectedRules);
+ }
+
+ private static Formula getInvalidFormula() {
+ return new Formula() {
+ @Override
+ public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
+ return false;
+ }
+
+ @Override
+ public int getTag() {
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj);
+ }
+
+ @NonNull
+ @Override
+ protected Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
+ };
+ }
+}
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 5903b5a..ad74901 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
@@ -91,7 +91,7 @@
}
@Test
- public void testXmlStream_serializeValidOpenFormula() throws Exception {
+ public void testXmlStream_serializeValidCompoundFormula() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -134,7 +134,7 @@
}
@Test
- public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception {
+ public void testXmlString_serializeValidCompoundFormula_notConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -174,7 +174,7 @@
}
@Test
- public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception {
+ public void testXmlString_serializeValidCompoundFormula_andConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
@@ -224,7 +224,7 @@
}
@Test
- public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception {
+ public void testXmlString_serializeValidCompoundFormula_orConnector() throws Exception {
Rule rule =
new Rule(
new CompoundFormula(
diff --git a/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
new file mode 100644
index 0000000..e54410b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.integrity.utils;
+
+public class TestUtils {
+
+ public static String getBits(int component, int numOfBits) {
+ return String.format("%" + numOfBits + "s", Integer.toBinaryString(component))
+ .replace(' ', '0');
+ }
+
+ public static String getValueBits(String value) {
+ StringBuilder stringBuilder = new StringBuilder();
+ for (byte valueByte : value.getBytes()) {
+ stringBuilder.append(getBits(valueByte, /* numOfBits= */ 8));
+ }
+ return stringBuilder.toString();
+ }
+
+ public static byte[] getBytes(String bits) {
+ int bitStringSize = bits.length();
+ int numOfBytes = bitStringSize / 8;
+ if (bitStringSize % 8 != 0) {
+ numOfBytes++;
+ }
+ byte[] bytes = new byte[numOfBytes];
+ for (int i = 0; i < bits.length(); i++) {
+ if (bits.charAt(i) == '1') {
+ bytes[i / 8] |= 1 << (7 - (i % 8));
+ }
+ }
+ return bytes;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 313afbb..dee79bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -263,6 +263,19 @@
}
@MediumTest
+ public void testFindExistingGuest_guestExists() throws Exception {
+ UserInfo userInfo1 = createUser("Guest", UserInfo.FLAG_GUEST);
+ UserInfo foundGuest = mUserManager.findCurrentGuestUser();
+ assertNotNull(foundGuest);
+ }
+
+ @SmallTest
+ public void testFindExistingGuest_guestDoesNotExist() throws Exception {
+ UserInfo foundGuest = mUserManager.findCurrentGuestUser();
+ assertNull(foundGuest);
+ }
+
+ @MediumTest
public void testSetUserAdmin() throws Exception {
UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 0ca62e2..81fb0ec 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -61,7 +61,6 @@
import android.os.PowerManager;
import android.os.PowerSaveState;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
@@ -97,10 +96,12 @@
* Tests for {@link com.android.server.power.PowerManagerService}
*/
public class PowerManagerServiceTest {
+ private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+ private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
private static final float PRECISION = 0.001f;
private static final float BRIGHTNESS_FACTOR = 0.7f;
private static final boolean BATTERY_SAVER_ENABLED = true;
- private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
@Mock private BatterySaverPolicy mBatterySaverPolicyMock;
@Mock private LightsManager mLightsManagerMock;
@@ -112,6 +113,7 @@
@Mock private Notifier mNotifierMock;
@Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+ @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
@Mock
private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@@ -159,6 +161,7 @@
when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
mDisplayPowerRequest = new DisplayPowerRequest();
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
@@ -218,6 +221,11 @@
InattentiveSleepWarningController createInattentiveSleepWarningController() {
return mInattentiveSleepWarningControllerMock;
}
+
+ @Override
+ public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+ return mSystemPropertiesMock;
+ }
});
return mService;
}
@@ -228,12 +236,6 @@
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
LocalServices.removeServiceForTest(BatteryManagerInternal.class);
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-
- Settings.Global.putInt(
- mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0);
- setAttentiveTimeout(-1);
- Settings.Global.putInt(mContextSpy.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
/**
@@ -322,10 +324,10 @@
@Test
public void testGetLastShutdownReasonInternal() {
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_REBOOT_REASON), any())).thenReturn(
+ "shutdown,thermal");
createService();
- SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "shutdown,thermal");
- int reason = mService.getLastShutdownReasonInternal(TEST_LAST_REBOOT_PROPERTY);
- SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "");
+ int reason = mService.getLastShutdownReasonInternal();
assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN);
}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
index 8a7edf7..7a0a28d 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
@@ -640,9 +640,9 @@
private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
TimestampedValue<Long> utcTime) {
- PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(phoneId);
- timeSuggestion.setUtcTime(utcTime);
- return timeSuggestion;
+ return new PhoneTimeSuggestion.Builder(phoneId)
+ .setUtcTime(utcTime)
+ .build();
}
private ManualTimeSuggestion createManualTimeSuggestion(long timeMillis) {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 9951e85..84b495f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -140,10 +140,10 @@
private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
int phoneId = 1234;
- PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId);
TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
- suggestion.setUtcTime(timeValue);
- return suggestion;
+ return new PhoneTimeSuggestion.Builder(phoneId)
+ .setUtcTime(timeValue)
+ .build();
}
private static ManualTimeSuggestion createManualTimeSuggestion() {
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index e32103f..e6bb244 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -45,6 +45,7 @@
import java.io.IOException;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -93,6 +94,8 @@
for (File f : usageFiles) {
f.delete();
}
+ } else {
+ intervalDir.delete();
}
}
}
@@ -587,6 +590,7 @@
db.readMappingsLocked();
db.init(1);
db.putUsageStats(interval, mIntervalStats);
+ db.writeMappingsLocked();
final String removedPackage = "fake.package.name0";
// invoke handler call directly from test to remove package
@@ -594,19 +598,19 @@
List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
mIntervalStatsVerifier);
- for (int i = 0; i < stats.size(); i++) {
- final IntervalStats stat = stats.get(i);
- if (stat.packageStats.containsKey(removedPackage)) {
- fail("Found removed package " + removedPackage + " in package stats.");
+ assertEquals(1, stats.size(),
+ "Only one interval stats object should exist for the given time range.");
+ final IntervalStats stat = stats.get(0);
+ if (stat.packageStats.containsKey(removedPackage)) {
+ fail("Found removed package " + removedPackage + " in package stats.");
+ return;
+ }
+ for (int i = 0; i < stat.events.size(); i++) {
+ final Event event = stat.events.get(i);
+ if (removedPackage.equals(event.mPackage)) {
+ fail("Found an event from removed package " + removedPackage);
return;
}
- for (int j = 0; j < stat.events.size(); j++) {
- final Event event = stat.events.get(j);
- if (removedPackage.equals(event.mPackage)) {
- fail("Found an event from removed package " + removedPackage);
- return;
- }
- }
}
}
@@ -617,4 +621,90 @@
verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
}
+
+ private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval,
+ String removedPackage) {
+ List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+ assertEquals(1, stats.size(),
+ "Only one interval stats object should exist for the given time range.");
+ final IntervalStats stat = stats.get(0);
+ if (stat.packageStats.containsKey(removedPackage)) {
+ fail("Found removed package " + removedPackage + " in package stats.");
+ return;
+ }
+ for (int i = 0; i < stat.events.size(); i++) {
+ final Event event = stat.events.get(i);
+ if (removedPackage.equals(event.mPackage)) {
+ fail("Found an event from removed package " + removedPackage);
+ return;
+ }
+ }
+ }
+
+ private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval,
+ Set<String> installedPackages) {
+ List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+ assertEquals(1, stats.size(),
+ "Only one interval stats object should exist for the given time range.");
+ final IntervalStats stat = stats.get(0);
+ if (!stat.packageStats.containsAll(installedPackages)) {
+ fail("Could not find some installed packages in package stats.");
+ return;
+ }
+ // attempt to find an event from each installed package
+ for (String installedPackage : installedPackages) {
+ for (int i = 0; i < stat.events.size(); i++) {
+ if (installedPackage.equals(stat.events.get(i).mPackage)) {
+ break;
+ }
+ if (i == stat.events.size() - 1) {
+ fail("Could not find any event for: " + installedPackage);
+ return;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPackageDataIsRemoved() throws IOException {
+ UsageStatsDatabase db = new UsageStatsDatabase(mTestDir);
+ db.readMappingsLocked();
+ db.init(1);
+
+ // write stats to disk for each interval
+ db.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
+ db.putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, mIntervalStats);
+ db.putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, mIntervalStats);
+ db.putUsageStats(UsageStatsManager.INTERVAL_YEARLY, mIntervalStats);
+ db.writeMappingsLocked();
+
+ final Set<String> installedPackages = mIntervalStats.packageStats.keySet();
+ final String removedPackage = installedPackages.iterator().next();
+ installedPackages.remove(removedPackage);
+
+ // mimic a package uninstall
+ db.onPackageRemoved(removedPackage, System.currentTimeMillis());
+
+ // mimic the idle prune job being triggered
+ db.pruneUninstalledPackagesData();
+
+ // read data from disk into a new db instance
+ UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+ newDB.readMappingsLocked();
+ newDB.init(mEndTime);
+
+ // query data for each interval and ensure data for package doesn't exist
+ verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, removedPackage);
+ verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, removedPackage);
+ verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, removedPackage);
+ verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, removedPackage);
+
+ // query data for each interval and ensure some data for installed packages exists
+ verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, installedPackages);
+ verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, installedPackages);
+ verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, installedPackages);
+ verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, installedPackages);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 5336811..de2bba2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -123,7 +123,7 @@
assertTrue(stack1.isFocusedStackOnDisplay());
// Stack2 should be focused after removing stack1.
- display.removeChild(stack1);
+ display.removeStack(stack1);
assertTrue(stack2.isFocusedStackOnDisplay());
}
@@ -224,7 +224,7 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(alwaysOnTopStack).build();
alwaysOnTopStack.setAlwaysOnTop(true);
- display.positionChildAtTop(alwaysOnTopStack, false /* includingParents */);
+ display.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
assertTrue(alwaysOnTopStack.isAlwaysOnTop());
// Ensure always on top state is synced to the children of the stack.
assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -238,36 +238,36 @@
final ActivityStack anotherAlwaysOnTopStack = display.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
- display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
- int topPosition = display.getChildCount() - 1;
+ int topPosition = display.getStackCount() - 1;
// Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
// existing alwaysOnTop stack.
- assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
+ assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1));
final ActivityStack nonAlwaysOnTopStack = display.createStack(
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(display, nonAlwaysOnTopStack.getDisplay());
- topPosition = display.getChildCount() - 1;
+ topPosition = display.getStackCount() - 1;
// Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
// existing other non-alwaysOnTop stacks.
- assertEquals(nonAlwaysOnTopStack, display.getChildAt(topPosition - 3));
+ assertEquals(nonAlwaysOnTopStack, display.getStackAt(topPosition - 3));
anotherAlwaysOnTopStack.setAlwaysOnTop(false);
- display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+ display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
// Ensure, when always on top is turned off for a stack, the stack is put just below all
// other always on top stacks.
- assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
+ assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2));
anotherAlwaysOnTopStack.setAlwaysOnTop(true);
// Ensure always on top state changes properly when windowing mode changes.
anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
- assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
+ assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2));
anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
- assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
+ assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1));
}
@Test
@@ -299,14 +299,14 @@
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
- display.positionChildAtTop(stack3, false);
+ display.positionStackAtTop(stack3, false);
return true;
}).when(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
any());
// Removing stacks from the display while removing stacks.
doAnswer(invocation -> {
- display.removeChild(stack2);
+ display.removeStack(stack2);
return true;
}).when(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c2b8827..7166829 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -455,7 +455,7 @@
verify(mService.getLifecycleManager()).scheduleTransaction(
eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected));
} finally {
- stack.getDisplay().removeChild(stack);
+ stack.getDisplay().removeStack(stack);
}
}
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 c8795ce..7806d40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -290,7 +290,7 @@
verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
// Also move display to back because there is only one stack left.
- display.removeChild(stack1);
+ display.removeStack(stack1);
stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
}
@@ -576,7 +576,7 @@
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -595,7 +595,7 @@
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -614,7 +614,7 @@
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -633,7 +633,7 @@
@Test
public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -660,7 +660,7 @@
@Test
public void
testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -684,7 +684,7 @@
@Test
public void testMoveHomeStackBehindStack_BehindHomeStack() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -707,7 +707,7 @@
@Test
public void testMoveHomeStackBehindStack() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -814,16 +814,16 @@
}
@SuppressWarnings("TypeParameterUnusedInFormals")
- private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+ private ActivityStack createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
final ActivityStack stack;
if (activityType == ACTIVITY_TYPE_HOME) {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
if (onTop) {
- mDefaultDisplay.positionChildAtTop(stack, false /* includingParents */);
+ mDefaultDisplay.positionStackAtTop(stack, false /* includingParents */);
} else {
- mDefaultDisplay.positionChildAtBottom(stack);
+ mDefaultDisplay.positionStackAtBottom(stack);
}
} else {
stack = new StackBuilder(mRootActivityContainer)
@@ -834,7 +834,7 @@
.setCreateActivity(true)
.build();
}
- return (T) stack;
+ return stack;
}
@Test
@@ -1051,7 +1051,7 @@
StackOrderChangedListener listener = new StackOrderChangedListener();
mDefaultDisplay.registerStackOrderChangedListener(listener);
try {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
} finally {
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
}
@@ -1060,12 +1060,12 @@
@Test
public void testStackOrderChangedOnAddPositionStack() {
- mDefaultDisplay.removeChild(mStack);
+ mDefaultDisplay.removeStack(mStack);
StackOrderChangedListener listener = new StackOrderChangedListener();
mDefaultDisplay.registerStackOrderChangedListener(listener);
try {
- mDefaultDisplay.addChild(mStack, 0);
+ mDefaultDisplay.addStack(mStack, 0);
} finally {
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
}
@@ -1080,7 +1080,7 @@
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultDisplay.registerStackOrderChangedListener(listener);
- mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
+ mDefaultDisplay.positionStackAtBottom(fullscreenStack1);
} finally {
mDefaultDisplay.unregisterStackOrderChangedListener(listener);
}
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 c712d6d..d5fdf98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -517,8 +517,8 @@
}
private void assertNoTasks(ActivityDisplay display) {
- for (int i = display.getChildCount() - 1; i >= 0; --i) {
- final ActivityStack stack = display.getChildAt(i);
+ for (int i = display.getStackCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = display.getStackAt(i);
assertThat(stack.getAllTasks()).isEmpty();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 7322ac3..0021cc5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -203,6 +203,15 @@
}
ActivityRecord build() {
+ try {
+ mService.deferWindowLayout();
+ return buildInner();
+ } finally {
+ mService.continueWindowLayout();
+ }
+ }
+
+ ActivityRecord buildInner() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
@@ -236,7 +245,7 @@
ActivityOptions options = null;
if (mLaunchTaskBehind) {
- options = ActivityOptions.makeTaskLaunchBehind();
+ options = ActivityOptions.makeTaskLaunchBehind();
}
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
@@ -399,11 +408,18 @@
return this;
}
+ // TODO(display-merge): Remove
StackBuilder setDisplay(ActivityDisplay display) {
mDisplay = display;
return this;
}
+ StackBuilder setDisplay(DisplayContent display) {
+ // TODO(display-merge): Remove cast
+ mDisplay = (ActivityDisplay) display;
+ return this;
+ }
+
StackBuilder setOnTop(boolean onTop) {
mOnTop = onTop;
return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index beec1a8..0ad0f95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -47,6 +47,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
@@ -63,6 +64,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class BoundsAnimationControllerTests extends WindowTestsBase {
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 73420a0..0aa6961 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -36,12 +36,14 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Build/Install/Run:
* atest FrameworksServicesTests:DimmerTests
*/
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DimmerTests extends WindowTestsBase {
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3eda980..716e777 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -709,13 +709,13 @@
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mActivityDisplay)
+ .setDisplay(dc)
.build();
doReturn(true).when(stack).isVisible();
final ActivityStack freeformStack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mActivityDisplay)
+ .setDisplay(dc)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
.build();
doReturn(true).when(freeformStack).isVisible();
@@ -743,7 +743,7 @@
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mActivityDisplay).build();
+ .setDisplay(dc).build();
final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
activity.setRequestedOrientation(newOrientation);
@@ -765,12 +765,13 @@
final ActivityStack stack =
new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mActivityDisplay).build();
+ .setDisplay(dc).build();
final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
activity.setRequestedOrientation(newOrientation);
- verify(dc.mActivityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
+ // TODO(display-merge): Remove cast
+ verify((ActivityDisplay) dc, never()).updateDisplayOverrideConfigurationLocked(any(),
eq(activity), anyBoolean(), same(null));
assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
}
@@ -940,6 +941,7 @@
final DisplayContent displayContent = createNewDisplay();
Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
+ doNothing().when(displayContent).preOnConfigurationChanged();
displayContent.onConfigurationChanged(newConfig);
@@ -959,12 +961,12 @@
Mockito.doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
final boolean[] continued = new boolean[1];
- spyOn(dc.mActivityDisplay);
+ // TODO(display-merge): Remove cast
Mockito.doAnswer(
invocation -> {
continued[0] = true;
return true;
- }).when(dc.mActivityDisplay).updateDisplayOverrideConfigurationLocked();
+ }).when((ActivityDisplay) dc).updateDisplayOverrideConfigurationLocked();
final boolean[] called = new boolean[1];
mWm.mDisplayRotationController =
new IDisplayWindowRotationController.Stub() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 6767465..f754c59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -32,9 +32,11 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
+import org.junit.runner.RunWith;
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
@Rule
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 79ad0c4..9e5d9da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -46,6 +46,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import java.io.File;
@@ -63,6 +64,7 @@
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class LaunchParamsPersisterTests extends ActivityTestsBase {
private static final int TEST_USER_ID = 3;
private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4f2d5d2..9f97c48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -977,9 +977,9 @@
private void assertNotRestoreTask(Runnable action) {
// Verify stack count doesn't change because task with fullscreen mode and standard type
// would have its own stack.
- final int orignalStackCount = mDisplay.getChildCount();
+ final int originalStackCount = mDisplay.getStackCount();
action.run();
- assertEquals(orignalStackCount, mDisplay.getChildCount());
+ assertEquals(originalStackCount, mDisplay.getStackCount());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 1abd366..2374847 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -138,7 +138,8 @@
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+ // TODO(display-merge): Remove cast
+ final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -163,7 +164,8 @@
@Test
public void testWallpaperIncluded_expectTarget() throws Exception {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+ // TODO(display-merge): Remove cast
+ final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final ActivityRecord homeAppWindow =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -192,7 +194,8 @@
@Test
public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+ // TODO(display-merge): Remove cast
+ final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -223,7 +226,8 @@
@Test
public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
mWm.setRecentsAnimationController(mController);
- final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+ // TODO(display-merge): Remove cast
+ final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
final ActivityRecord homeActivity =
new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index e67380c..4abab63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -119,7 +119,7 @@
final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
final ActivityStack homeStack =
defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
- defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+ defaultDisplay.positionStackAtTop(homeStack, false /* includingParents */);
ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
if (topRunningHomeActivity == null) {
topRunningHomeActivity = new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index e353903..112479b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -54,6 +54,7 @@
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;
@@ -64,6 +65,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class RemoteAnimationControllerTest extends WindowTestsBase {
@Mock SurfaceControl mMockLeash;
@@ -84,7 +86,7 @@
when(mMockRunner.asBinder()).thenReturn(new Binder());
mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
mAdapter.setCallingPidUid(123, 456);
- mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0);
+ runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
}
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 4257249..59c7c02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -226,20 +226,20 @@
@Test
public void testRemovingStackOnAppCrash() {
final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
- final int originalStackCount = defaultDisplay.getChildCount();
+ final int originalStackCount = defaultDisplay.getStackCount();
final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+ assertEquals(originalStackCount + 1, defaultDisplay.getStackCount());
// Let's pretend that the app has crashed.
firstActivity.app.setThread(null);
mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
// Verify that the stack was removed.
- assertEquals(originalStackCount, defaultDisplay.getChildCount());
+ assertEquals(originalStackCount, defaultDisplay.getStackCount());
}
@Test
@@ -392,7 +392,7 @@
ACTIVITY_TYPE_STANDARD, false /* onTop */));
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
- display.positionChildAtBottom(targetStack);
+ display.positionStackAtBottom(targetStack);
// Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
// is the current top focused stack.
@@ -493,7 +493,7 @@
final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
activity.setState(ActivityState.RESUMED, "test");
- display.positionChildAtBottom(targetStack);
+ display.positionStackAtBottom(targetStack);
// Assume the stack is at the topmost position
assertFalse(targetStack.isTopStackOnDisplay());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index eba2bc8..5c9ccae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -74,7 +74,7 @@
final int numTasks = 10;
int activeTime = 0;
for (int i = 0; i < numTasks; i++) {
- createTask(display.getChildAt(i % numStacks), ".Task" + i, i, activeTime++);
+ createTask(display.getStackAt(i % numStacks), ".Task" + i, i, activeTime++);
}
// Ensure that the latest tasks were returned in order of decreasing last active time,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index c483489..3008a75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -50,6 +50,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -63,6 +64,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class SurfaceAnimationRunnerTest extends WindowTestsBase {
@Mock SurfaceControl mMockSurface;
@@ -98,6 +100,7 @@
verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f));
+ waitHandlerIdle(SurfaceAnimationThread.getHandler());
mFinishCallbackLatch.await(1, SECONDS);
assertFinishCallbackCalled();
@@ -176,6 +179,7 @@
assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
mSurfaceAnimationRunner.continueStartingAnimations();
waitUntilNextFrame();
+ waitHandlerIdle(SurfaceAnimationThread.getHandler());
assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
mFinishCallbackLatch.await(1, SECONDS);
assertFinishCallbackCalled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 979aab6..2894241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -44,6 +44,7 @@
import org.junit.After;
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;
@@ -56,6 +57,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class SurfaceAnimatorTest extends WindowTestsBase {
@Mock AnimationAdapter mSpec;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 27ebd5a..9a91efe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -53,6 +53,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
@@ -66,6 +67,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
private static final Rect DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
/* right */ 1920, /* bottom */ 1080);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 9275512..8970571 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -19,7 +19,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
import static com.android.server.wm.WindowManagerService.dipToPixel;
@@ -33,7 +32,6 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.app.IActivityTaskManager;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
@@ -46,6 +44,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link TaskPositioner} class.
@@ -55,6 +54,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskPositionerTests extends WindowTestsBase {
private static final boolean DEBUGGING = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index f595e05..0274b7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -18,21 +18,21 @@
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static junit.framework.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.ActivityManager.TaskSnapshot;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
-import android.app.ActivityManager.TaskSnapshot;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Test class for {@link TaskSnapshotCache}.
@@ -42,6 +42,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
private TaskSnapshotCache mCache;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 3b11003..6ebaf29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -33,6 +33,7 @@
import com.google.android.collect.Sets;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link TaskSnapshotController}.
@@ -42,6 +43,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskSnapshotControllerTest extends WindowTestsBase {
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index b29453a..b5a5790 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -38,6 +38,7 @@
import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.util.function.Predicate;
@@ -50,6 +51,7 @@
*/
@MediumTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 74db820..817344f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -47,6 +47,7 @@
import com.android.server.wm.TaskSnapshotSurface.Window;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class for {@link TaskSnapshotSurface}.
@@ -56,6 +57,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class TaskSnapshotSurfaceTest extends WindowTestsBase {
private TaskSnapshotSurface mSurface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index dbf61db..6ad9f74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -19,6 +19,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -112,6 +115,10 @@
@Test
public void testDisplayPositionWithPinnedStack() {
+ // Make sure the display is system owned display which capable to move the stack to top.
+ spyOn(mDisplayContent);
+ doReturn(false).when(mDisplayContent).isUntrustedVirtualDisplay();
+
// The display contains pinned stack that was added in {@link #setUp}.
final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 64ac547..c359fb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -48,6 +48,7 @@
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test class to for {@link android.app.WindowConfiguration}.
@@ -57,6 +58,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowConfigurationTests extends WindowTestsBase {
private Rect mParentBounds;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 8140045..13cf22d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -56,6 +56,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
import java.util.Comparator;
@@ -68,6 +69,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowContainerTests extends WindowTestsBase {
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 4b666f5..8936aad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
import java.util.function.Consumer;
@@ -42,6 +43,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowContainerTraversalTests extends WindowTestsBase {
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 1c9eed2..ce6efdf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -32,6 +32,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Test for {@link WindowManagerService.SettingsObserver}.
@@ -40,6 +41,7 @@
* atest WmTests:WindowManagerSettingsTests
*/
@SmallTest
+@RunWith(WindowTestRunner.class)
public class WindowManagerSettingsTests extends WindowTestsBase {
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index eed5ef5..72baedb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -222,7 +222,8 @@
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
- // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target.
+ // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being
+ // an IME target.
appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
@@ -230,7 +231,7 @@
appWindow.setHasSurface(true);
imeWindow.setHasSurface(true);
- // Windows with FLAG_NOT_FOCUSABLE can't be IME targets
+ // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets
assertFalse(appWindow.canBeImeTarget());
assertFalse(imeWindow.canBeImeTarget());
@@ -238,16 +239,10 @@
appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
- // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME
- // target while an IME window can never be an IME target regardless of its visibility
- // or flags.
- assertFalse(appWindow.canBeImeTarget());
- assertFalse(imeWindow.canBeImeTarget());
-
- appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
- assertFalse(appWindow.canBeImeTarget());
- appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
+ // Visible app window with flags can be IME target while an IME window can never be an IME
+ // target regardless of its visibility or flags.
assertTrue(appWindow.canBeImeTarget());
+ assertFalse(imeWindow.canBeImeTarget());
// Make windows invisible
appWindow.hideLw(false /* doAnimation */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 437b84b..22ba90b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -320,7 +320,7 @@
synchronized (mWm.mGlobalLock) {
return new ActivityTestsBase.StackBuilder(
dc.mWmService.mAtmService.mRootActivityContainer)
- .setDisplay(dc.mActivityDisplay)
+ .setDisplay(dc)
.setWindowingMode(windowingMode)
.setActivityType(activityType)
.setCreateActivity(false)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 4cdbea0..e6aed49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -31,6 +31,7 @@
import androidx.test.filters.SmallTest;
import org.junit.Test;
+import org.junit.runner.RunWith;
/**
* Tests for the {@link WindowToken} class.
@@ -40,6 +41,7 @@
*/
@SmallTest
@Presubmit
+@RunWith(WindowTestRunner.class)
public class WindowTokenTests extends WindowTestsBase {
@Test
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 46b261b..8fb283a 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -448,8 +448,11 @@
/**
* Parses all of the tokens to strings in the obfuscated usage stats data. This includes
* deobfuscating each of the package tokens and chooser actions and categories.
+ *
+ * @return {@code true} if any stats were omitted while deobfuscating, {@code false} otherwise.
*/
- private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+ private boolean deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+ boolean dataOmitted = false;
final int usageStatsSize = packageStatsObfuscated.size();
for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
@@ -457,6 +460,7 @@
usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
if (usageStats.mPackageName == null) {
Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
+ dataOmitted = true;
continue;
}
@@ -489,14 +493,18 @@
}
packageStats.put(usageStats.mPackageName, usageStats);
}
+ return dataOmitted;
}
/**
* Parses all of the tokens to strings in the obfuscated events data. This includes
* deobfuscating the package token, along with any class, task root package/class tokens, and
* shortcut or notification channel tokens.
+ *
+ * @return {@code true} if any events were omitted while deobfuscating, {@code false} otherwise.
*/
- private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
+ private boolean deobfuscateEvents(PackagesTokenData packagesTokenData) {
+ boolean dataOmitted = false;
for (int i = this.events.size() - 1; i >= 0; i--) {
final Event event = this.events.get(i);
final int packageToken = event.mPackageToken;
@@ -504,6 +512,7 @@
if (event.mPackage == null) {
Slog.e(TAG, "Unable to parse event package " + packageToken);
this.events.remove(i);
+ dataOmitted = true;
continue;
}
@@ -543,6 +552,7 @@
Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
+ " for package " + packageToken);
this.events.remove(i);
+ dataOmitted = true;
continue;
}
break;
@@ -554,21 +564,25 @@
+ event.mNotificationChannelIdToken + " for package "
+ packageToken);
this.events.remove(i);
+ dataOmitted = true;
continue;
}
break;
}
}
+ return dataOmitted;
}
/**
* Parses the obfuscated tokenized data held in this interval stats object.
*
+ * @return {@code true} if any data was omitted while deobfuscating, {@code false} otherwise.
* @hide
*/
- public void deobfuscateData(PackagesTokenData packagesTokenData) {
- deobfuscateUsageStats(packagesTokenData);
- deobfuscateEvents(packagesTokenData);
+ public boolean deobfuscateData(PackagesTokenData packagesTokenData) {
+ final boolean statsOmitted = deobfuscateUsageStats(packagesTokenData);
+ final boolean eventsOmitted = deobfuscateEvents(packagesTokenData);
+ return statsOmitted || eventsOmitted;
}
/**
diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java
index 4bf08a4..f19abbb 100644
--- a/services/usage/java/com/android/server/usage/PackagesTokenData.java
+++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java
@@ -162,15 +162,18 @@
*
* @param packageName the package to be removed
* @param timeRemoved the time stamp of when the package was removed
+ * @return the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN}
+ * if not mapped
*/
- public void removePackage(String packageName, long timeRemoved) {
+ public int removePackage(String packageName, long timeRemoved) {
removedPackagesMap.put(packageName, timeRemoved);
if (!packagesToTokensMap.containsKey(packageName)) {
- return;
+ return UNASSIGNED_TOKEN;
}
final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
packagesToTokensMap.remove(packageName);
tokensToPackagesMap.delete(packageToken);
+ return packageToken;
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 27d7360..e34824c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -29,6 +29,7 @@
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import libcore.io.IoUtils;
@@ -52,6 +53,7 @@
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
/**
@@ -122,6 +124,7 @@
private int mCurrentVersion;
private boolean mFirstUpdate;
private boolean mNewUpdate;
+ private boolean mUpgradePerformed;
// The obfuscated packages to tokens mappings file
private final File mPackageMappingsFile;
@@ -325,6 +328,13 @@
return mNewUpdate;
}
+ /**
+ * Was an upgrade performed when this database was initialized?
+ */
+ boolean wasUpgradePerformed() {
+ return mUpgradePerformed;
+ }
+
private void checkVersionAndBuildLocked() {
int version;
String buildFingerprint;
@@ -397,6 +407,8 @@
if (mUpdateBreadcrumb.exists()) {
// Files should be up to date with current version. Clear the version update breadcrumb
mUpdateBreadcrumb.delete();
+ // update mUpgradePerformed after breadcrumb is deleted to indicate a successful upgrade
+ mUpgradePerformed = true;
}
if (mBackupsDir.exists() && !KEEP_BACKUP_DIR) {
@@ -545,12 +557,127 @@
}
}
- void onPackageRemoved(String packageName, long timeRemoved) {
+ /**
+ * Returns the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN}
+ * if not mapped.
+ */
+ int onPackageRemoved(String packageName, long timeRemoved) {
synchronized (mLock) {
- mPackagesTokenData.removePackage(packageName, timeRemoved);
+ final int tokenRemoved = mPackagesTokenData.removePackage(packageName, timeRemoved);
+ try {
+ writeMappingsLocked();
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to update package mappings on disk after removing token "
+ + tokenRemoved);
+ }
+ return tokenRemoved;
}
}
+ /**
+ * Reads all the usage stats data on disk and rewrites it with any data related to uninstalled
+ * packages omitted. Returns {@code true} on success, {@code false} otherwise.
+ */
+ boolean pruneUninstalledPackagesData() {
+ synchronized (mLock) {
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ final File[] files = mIntervalDirs[i].listFiles();
+ if (files == null) {
+ continue;
+ }
+ for (int j = 0; j < files.length; j++) {
+ try {
+ final IntervalStats stats = new IntervalStats();
+ final AtomicFile atomicFile = new AtomicFile(files[j]);
+ if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData)) {
+ continue; // no data was omitted when read so no need to rewrite
+ }
+ // Any data related to packages that have been removed would have failed
+ // the deobfuscation step on read so the IntervalStats object here only
+ // contains data for packages that are currently installed - all we need
+ // to do here is write the data back to disk.
+ writeLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to prune data from: " + files[j].toString());
+ return false;
+ }
+ }
+ }
+
+ try {
+ writeMappingsLocked();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write package mappings after pruning data.");
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Iterates through all the files on disk and prunes any data that belongs to packages that have
+ * been uninstalled (packages that are not in the given list).
+ * Note: this should only be called once, when there has been a database upgrade.
+ *
+ * @param installedPackages map of installed packages (package_name:package_install_time)
+ */
+ void prunePackagesDataOnUpgrade(HashMap<String, Long> installedPackages) {
+ if (ArrayUtils.isEmpty(installedPackages)) {
+ return;
+ }
+ synchronized (mLock) {
+ for (int i = 0; i < mIntervalDirs.length; i++) {
+ final File[] files = mIntervalDirs[i].listFiles();
+ if (files == null) {
+ continue;
+ }
+ for (int j = 0; j < files.length; j++) {
+ try {
+ final IntervalStats stats = new IntervalStats();
+ final AtomicFile atomicFile = new AtomicFile(files[j]);
+ readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+ if (!pruneStats(installedPackages, stats)) {
+ continue; // no stats were pruned so no need to rewrite
+ }
+ writeLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to prune data from: " + files[j].toString());
+ }
+ }
+ }
+ }
+ }
+
+ private boolean pruneStats(HashMap<String, Long> installedPackages, IntervalStats stats) {
+ boolean dataPruned = false;
+
+ // prune old package usage stats
+ for (int i = stats.packageStats.size() - 1; i >= 0; i--) {
+ final UsageStats usageStats = stats.packageStats.valueAt(i);
+ final Long timeInstalled = installedPackages.get(usageStats.mPackageName);
+ if (timeInstalled == null || timeInstalled > usageStats.mEndTimeStamp) {
+ stats.packageStats.removeAt(i);
+ dataPruned = true;
+ }
+ }
+ if (dataPruned) {
+ // ensure old stats don't linger around during the obfuscation step on write
+ stats.packageStatsObfuscated.clear();
+ }
+
+ // prune old events
+ for (int i = stats.events.size() - 1; i >= 0; i--) {
+ final UsageEvents.Event event = stats.events.get(i);
+ final Long timeInstalled = installedPackages.get(event.mPackage);
+ if (timeInstalled == null || timeInstalled > event.mTimeStamp) {
+ stats.events.remove(i);
+ dataPruned = true;
+ }
+ }
+
+ return dataPruned;
+ }
+
public void onTimeChanged(long timeDiffMillis) {
synchronized (mLock) {
StringBuilder logBuilder = new StringBuilder();
@@ -645,7 +772,6 @@
}
// filter out events
- final int eventsSize = stats.events.size();
for (int i = stats.events.size() - 1; i >= 0; i--) {
final UsageEvents.Event event = stats.events.get(i);
final Long timeRemoved = removedPackagesMap.get(event.mPackage);
@@ -942,13 +1068,17 @@
readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
}
- private static void readLocked(AtomicFile file, IntervalStats statsOut, int version,
+ /**
+ * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+ */
+ private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
PackagesTokenData packagesTokenData) throws IOException {
+ boolean dataOmitted = false;
try {
FileInputStream in = file.openRead();
try {
statsOut.beginTime = parseBeginTime(file);
- readLocked(in, statsOut, version, packagesTokenData);
+ dataOmitted = readLocked(in, statsOut, version, packagesTokenData);
statsOut.lastTimeSaved = file.getLastModifiedTime();
} finally {
try {
@@ -961,10 +1091,15 @@
Slog.e(TAG, "UsageStatsDatabase", e);
throw e;
}
+ return dataOmitted;
}
- private static void readLocked(InputStream in, IntervalStats statsOut, int version,
+ /**
+ * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+ */
+ private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
PackagesTokenData packagesTokenData) throws IOException {
+ boolean dataOmitted = false;
switch (version) {
case 1:
case 2:
@@ -989,14 +1124,14 @@
} catch (IOException e) {
Slog.e(TAG, "Unable to read interval stats from proto.", e);
}
- statsOut.deobfuscateData(packagesTokenData);
+ dataOmitted = statsOut.deobfuscateData(packagesTokenData);
break;
default:
throw new RuntimeException(
"Unhandled UsageStatsDatabase version: " + Integer.toString(version)
+ " on read.");
}
-
+ return dataOmitted;
}
/**
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
new file mode 100644
index 0000000..4468871
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
@@ -0,0 +1,92 @@
+/*
+ * 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.usage;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.PersistableBundle;
+
+import com.android.server.LocalServices;
+
+/**
+ * JobService used to do any work for UsageStats while the device is idle.
+ */
+public class UsageStatsIdleService extends JobService {
+
+ /**
+ * Base job ID for the pruning job - must be unique within the system server uid.
+ */
+ private static final int PRUNE_JOB_ID = 546357475;
+
+ private static final String USER_ID_KEY = "user_id";
+
+ static void scheduleJob(Context context, int userId) {
+ final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+ final ComponentName component = new ComponentName(context.getPackageName(),
+ UsageStatsIdleService.class.getName());
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(USER_ID_KEY, userId);
+ final JobInfo pruneJob = new JobInfo.Builder(userJobId, component)
+ .setRequiresDeviceIdle(true)
+ .setExtras(bundle)
+ .setPersisted(true)
+ .build();
+
+ final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ final JobInfo pendingPruneJob = jobScheduler.getPendingJob(userJobId);
+ // only schedule a new prune job if one doesn't exist already for this user
+ if (!pruneJob.equals(pendingPruneJob)) {
+ jobScheduler.cancel(userJobId); // cancel any previously scheduled prune job
+ jobScheduler.schedule(pruneJob);
+ }
+
+ }
+
+ static void cancelJob(Context context, int userId) {
+ final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+ final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ jobScheduler.cancel(userJobId);
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ final PersistableBundle bundle = params.getExtras();
+ final int userId = bundle.getInt(USER_ID_KEY, -1);
+ if (userId == -1) {
+ return false;
+ }
+
+ AsyncTask.execute(() -> {
+ final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
+ UsageStatsManagerInternal.class);
+ final boolean pruned = usageStatsManagerInternal.pruneUninstalledPackagesData(userId);
+ jobFinished(params, !pruned); // reschedule if data was not pruned
+ });
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ // Since the pruning job isn't a heavy job, we don't want to cancel it's execution midway.
+ return false;
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ba4a448..0649223 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -52,6 +52,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
@@ -99,6 +100,7 @@
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -318,6 +320,8 @@
}
private void onUserUnlocked(int userId) {
+ // fetch the installed packages outside the lock so it doesn't block package manager.
+ final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
synchronized (mLock) {
// Create a user unlocked event to report
final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
@@ -334,9 +338,10 @@
}
boolean needToFlush = !pendingEvents.isEmpty();
+ initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
+ installedPackages);
mUserUnlockedStates.put(userId, true);
- final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
- userId, System.currentTimeMillis());
+ final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
if (userService == null) {
Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
return;
@@ -361,6 +366,29 @@
}
}
+ /**
+ * Fetches a map (package_name:install_time) of installed packages for the given user. This
+ * map contains all installed packages, including those packages which have been uninstalled
+ * with the DONT_DELETE_DATA flag.
+ * This is a helper method which should only be called when the given user's usage stats service
+ * is initialized; it performs a heavy query to package manager so do not call it otherwise.
+ * <br/>
+ * Note: DO NOT call this while holding the usage stats lock ({@code mLock}).
+ */
+ private HashMap<String, Long> getInstalledPackages(int userId) {
+ if (mPackageManager == null) {
+ return null;
+ }
+ final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ final HashMap<String, Long> packagesMap = new HashMap<>();
+ for (int i = installedPackages.size() - 1; i >= 0; i--) {
+ final PackageInfo packageInfo = installedPackages.get(i);
+ packagesMap.put(packageInfo.packageName, packageInfo.firstInstallTime);
+ }
+ return packagesMap;
+ }
+
private DevicePolicyManagerInternal getDpmInternal() {
if (mDpmInternal == null) {
mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
@@ -450,31 +478,42 @@
}
}
- private UserUsageStatsService getUserDataAndInitializeIfNeededLocked(int userId,
- long currentTimeMillis) {
- UserUsageStatsService service = mUserState.get(userId);
+ /**
+ * This should the be only way to fetch the usage stats service for a specific user.
+ */
+ private UserUsageStatsService getUserUsageStatsServiceLocked(int userId) {
+ final UserUsageStatsService service = mUserState.get(userId);
if (service == null) {
- final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
- "usagestats");
- service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this);
- if (mUserUnlockedStates.get(userId)) {
- try {
- service.init(currentTimeMillis);
- mUserState.put(userId, service);
- } catch (Exception e) {
- if (mUserManager.isUserUnlocked(userId)) {
- throw e; // rethrow exception - user is unlocked
- } else {
- Slog.w(TAG, "Attempted to initialize service for "
- + "stopped or removed user " + userId);
- return null;
- }
- }
- }
+ Slog.wtf(TAG, "Failed to fetch usage stats service for user " + userId + ". "
+ + "The user might not have been initialized yet.");
}
return service;
}
+ /**
+ * Initializes the given user's usage stats service - this should ideally only be called once,
+ * when the user is initially unlocked.
+ */
+ private void initializeUserUsageStatsServiceLocked(int userId,
+ long currentTimeMillis, HashMap<String, Long> installedPackages) {
+ final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
+ "usagestats");
+ final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
+ usageStatsDir, this);
+ try {
+ service.init(currentTimeMillis, installedPackages);
+ mUserState.put(userId, service);
+ } catch (Exception e) {
+ if (mUserManager.isUserUnlocked(userId)) {
+ Slog.w(TAG, "Failed to initialized unlocked user " + userId);
+ throw e; // rethrow the exception - user is unlocked
+ } else {
+ Slog.w(TAG, "Attempted to initialize service for stopped or removed user "
+ + userId);
+ }
+ }
+ }
+
private void migrateStatsToSystemCeIfNeededLocked(int userId) {
final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
"usagestats");
@@ -694,7 +733,6 @@
return;
}
- final long timeNow = System.currentTimeMillis();
final long elapsedRealtime = SystemClock.elapsedRealtime();
if (event.mPackage != null
@@ -789,8 +827,7 @@
break;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return; // user was stopped or removed
}
@@ -841,15 +878,18 @@
mAppStandby.onUserRemoved(userId);
mAppTimeLimit.onUserRemoved(userId);
}
+ // Cancel any scheduled jobs for this user since the user is being removed.
+ UsageStatsIdleService.cancelJob(getContext(), userId);
}
/**
* Called by the Handler for message MSG_PACKAGE_REMOVED.
*/
private void onPackageRemoved(int userId, String packageName) {
+ final int tokenRemoved;
synchronized (mLock) {
final long timeRemoved = System.currentTimeMillis();
- if (!mUserUnlockedStates.get(userId, false)) {
+ if (!mUserUnlockedStates.get(userId)) {
// If user is not unlocked and a package is removed for them, we will handle it
// when the user service is initialized and package manager is queried.
return;
@@ -859,7 +899,30 @@
return;
}
- userService.onPackageRemoved(packageName, timeRemoved);
+ tokenRemoved = userService.onPackageRemoved(packageName, timeRemoved);
+ }
+
+ // Schedule a job to prune any data related to this package.
+ if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) {
+ UsageStatsIdleService.scheduleJob(getContext(), userId);
+ }
+ }
+
+ /**
+ * Called by the Binder stub.
+ */
+ private boolean pruneUninstalledPackagesData(int userId) {
+ synchronized (mLock) {
+ if (!mUserUnlockedStates.get(userId)) {
+ return false; // user is no longer unlocked
+ }
+
+ final UserUsageStatsService userService = mUserState.get(userId);
+ if (userService == null) {
+ return false; // user was stopped or removed
+ }
+
+ return userService.pruneUninstalledPackagesData();
}
}
@@ -874,8 +937,7 @@
return null;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return null; // user was stopped or removed
}
@@ -909,8 +971,7 @@
return null;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return null; // user was stopped or removed
}
@@ -929,8 +990,7 @@
return null;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return null; // user was stopped or removed
}
@@ -949,8 +1009,7 @@
return null;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return null; // user was stopped or removed
}
@@ -969,8 +1028,7 @@
return null;
}
- final UserUsageStatsService service =
- getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
if (service == null) {
return null; // user was stopped or removed
}
@@ -2025,8 +2083,7 @@
// Check to ensure that only user 0's data is b/r for now
if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
- user, System.currentTimeMillis());
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
if (userStats == null) {
return null; // user was stopped or removed
}
@@ -2046,8 +2103,7 @@
}
if (user == UserHandle.USER_SYSTEM) {
- final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
- user, System.currentTimeMillis());
+ final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
if (userStats == null) {
return; // user was stopped or removed
}
@@ -2108,6 +2164,11 @@
public AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user) {
return mAppTimeLimit.getAppUsageLimit(packageName, user);
}
+
+ @Override
+ public boolean pruneUninstalledPackagesData(int userId) {
+ return UsageStatsService.this.pruneUninstalledPackagesData(userId);
+ }
}
private class MyPackageMonitor extends PackageMonitor {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index c6a5fcf..179b649 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,10 +34,7 @@
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
-import android.os.Process;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -46,8 +43,8 @@
import android.util.Slog;
import android.util.SparseIntArray;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.LocalServices;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
import java.io.File;
@@ -55,7 +52,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.List;
/**
@@ -112,9 +109,12 @@
mSystemTimeSnapshot = System.currentTimeMillis();
}
- void init(final long currentTimeMillis) {
- readPackageMappingsLocked();
+ void init(final long currentTimeMillis, HashMap<String, Long> installedPackages) {
+ readPackageMappingsLocked(installedPackages);
mDatabase.init(currentTimeMillis);
+ if (mDatabase.wasUpgradePerformed()) {
+ mDatabase.prunePackagesDataOnUpgrade(installedPackages);
+ }
int nullCount = 0;
for (int i = 0; i < mCurrentStats.length; i++) {
@@ -170,52 +170,53 @@
persistActiveStats();
}
- void onPackageRemoved(String packageName, long timeRemoved) {
- mDatabase.onPackageRemoved(packageName, timeRemoved);
+ int onPackageRemoved(String packageName, long timeRemoved) {
+ return mDatabase.onPackageRemoved(packageName, timeRemoved);
}
- private void readPackageMappingsLocked() {
+ private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
mDatabase.readMappingsLocked();
- cleanUpPackageMappingsLocked();
+ updatePackageMappingsLocked(installedPackages);
}
/**
- * Queries Package Manager for a list of installed packages and removes those packages from
- * mPackagesTokenData which are not installed any more.
+ * Queries Job Scheduler for any pending data prune jobs and if any exist, it updates the
+ * package mappings in memory by removing those tokens.
* This will only happen once per device boot, when the user is unlocked for the first time.
+ *
+ * @param installedPackages map of installed packages (package_name:package_install_time)
*/
- private void cleanUpPackageMappingsLocked() {
- final long timeNow = System.currentTimeMillis();
- /*
- Note (b/142501248): PackageManagerInternal#getInstalledApplications is not lightweight.
- Once its implementation is updated, or it's replaced with a better alternative, update
- the call here to use it. For now, using the heavy #getInstalledApplications is okay since
- this clean-up is only performed once every boot.
- */
- final PackageManagerInternal packageManagerInternal =
- LocalServices.getService(PackageManagerInternal.class);
- if (packageManagerInternal == null) {
+ private void updatePackageMappingsLocked(HashMap<String, Long> installedPackages) {
+ if (ArrayUtils.isEmpty(installedPackages)) {
return;
}
- final List<ApplicationInfo> installedPackages =
- packageManagerInternal.getInstalledApplications(0, mUserId, Process.SYSTEM_UID);
- // convert the package list to a set for easy look-ups
- final HashSet<String> packagesSet = new HashSet<>(installedPackages.size());
- for (int i = installedPackages.size() - 1; i >= 0; i--) {
- packagesSet.add(installedPackages.get(i).packageName);
- }
- final List<String> removedPackages = new ArrayList<>();
+
+ final long timeNow = System.currentTimeMillis();
+ final ArrayList<String> removedPackages = new ArrayList<>();
// populate list of packages that are found in the mappings but not in the installed list
for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
- if (!packagesSet.contains(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i))) {
- removedPackages.add(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i));
+ final String packageName = mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i);
+ if (!installedPackages.containsKey(packageName)) {
+ removedPackages.add(packageName);
}
}
+ if (removedPackages.isEmpty()) {
+ return;
+ }
- // remove packages in the mappings that are no longer installed
+ // remove packages in the mappings that are no longer installed and persist to disk
for (int i = removedPackages.size() - 1; i >= 0; i--) {
mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
}
+ try {
+ mDatabase.writeMappingsLocked();
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
+ }
+ }
+
+ boolean pruneUninstalledPackagesData() {
+ return mDatabase.pruneUninstalledPackagesData();
}
private void onTimeChanged(long oldTime, long newTime) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a0739c4..86ad795 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -291,7 +291,6 @@
*/
public static final int DIRECTION_OUTGOING = 1;
-
/** Call can currently be put on hold or unheld. */
public static final int CAPABILITY_HOLD = 0x00000001;
@@ -571,6 +570,7 @@
private final Bundle mIntentExtras;
private final long mCreationTimeMillis;
private final @CallDirection int mCallDirection;
+ private final @Connection.VerificationStatus int mCallerNumberVerificationStatus;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -880,6 +880,15 @@
return mCallDirection;
}
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -901,7 +910,9 @@
areBundlesEqual(mExtras, d.mExtras) &&
areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
- Objects.equals(mCallDirection, d.mCallDirection);
+ Objects.equals(mCallDirection, d.mCallDirection) &&
+ Objects.equals(mCallerNumberVerificationStatus,
+ d.mCallerNumberVerificationStatus);
}
return false;
}
@@ -923,7 +934,8 @@
mExtras,
mIntentExtras,
mCreationTimeMillis,
- mCallDirection);
+ mCallDirection,
+ mCallerNumberVerificationStatus);
}
/** {@hide} */
@@ -944,7 +956,8 @@
Bundle extras,
Bundle intentExtras,
long creationTimeMillis,
- int callDirection) {
+ int callDirection,
+ int callerNumberVerificationStatus) {
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -962,6 +975,7 @@
mIntentExtras = intentExtras;
mCreationTimeMillis = creationTimeMillis;
mCallDirection = callDirection;
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
}
/** {@hide} */
@@ -983,7 +997,8 @@
parcelableCall.getExtras(),
parcelableCall.getIntentExtras(),
parcelableCall.getCreationTimeMillis(),
- parcelableCall.getCallDirection());
+ parcelableCall.getCallDirection(),
+ parcelableCall.getCallerNumberVerificationStatus());
}
@Override
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4c22ba9..c063279 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -35,9 +36,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.telephony.Annotation.RilRadioTechnology;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsStreamMediaProfile;
import android.util.ArraySet;
import android.view.Surface;
@@ -50,6 +49,8 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.channels.Channels;
import java.util.ArrayList;
import java.util.Arrays;
@@ -153,6 +154,32 @@
public static final int STATE_PULLING_CALL = 7;
/**
+ * Indicates that the network could not perform verification.
+ */
+ public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+ /**
+ * Indicates that verification by the network passed. This indicates there is a high likelihood
+ * that the call originated from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_PASSED = 1;
+
+ /**
+ * Indicates that verification by the network failed. This indicates there is a high likelihood
+ * that the call did not originate from a valid source.
+ */
+ public static final int VERIFICATION_STATUS_FAILED = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+ VERIFICATION_STATUS_NOT_VERIFIED,
+ VERIFICATION_STATUS_PASSED,
+ VERIFICATION_STATUS_FAILED
+ })
+ public @interface VerificationStatus {}
+
+ /**
* Connection can currently be put on hold or unheld. This is distinct from
* {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
* it does not at the moment support the function. This can be true while the call is in the
@@ -475,6 +502,52 @@
//**********************************************************************************************
/**
+ * Define IMS Audio Codec
+ */
+ // Current audio codec is NONE
+ public static final int AUDIO_CODEC_NONE = ImsStreamMediaProfile.AUDIO_QUALITY_NONE; // 0
+ // Current audio codec is AMR
+ public static final int AUDIO_CODEC_AMR = ImsStreamMediaProfile.AUDIO_QUALITY_AMR; // 1
+ // Current audio codec is AMR_WB
+ public static final int AUDIO_CODEC_AMR_WB = ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB; // 2
+ // Current audio codec is QCELP13K
+ public static final int AUDIO_CODEC_QCELP13K = ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K; //3
+ // Current audio codec is EVRC
+ public static final int AUDIO_CODEC_EVRC = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC; // 4
+ // Current audio codec is EVRC_B
+ public static final int AUDIO_CODEC_EVRC_B = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B; // 5
+ // Current audio codec is EVRC_WB
+ public static final int AUDIO_CODEC_EVRC_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB; // 6
+ // Current audio codec is EVRC_NW
+ public static final int AUDIO_CODEC_EVRC_NW = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW; // 7
+ // Current audio codec is GSM_EFR
+ public static final int AUDIO_CODEC_GSM_EFR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR; // 8
+ // Current audio codec is GSM_FR
+ public static final int AUDIO_CODEC_GSM_FR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR; // 9
+ // Current audio codec is GSM_HR
+ public static final int AUDIO_CODEC_GSM_HR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR; // 10
+ // Current audio codec is G711U
+ public static final int AUDIO_CODEC_G711U = ImsStreamMediaProfile.AUDIO_QUALITY_G711U; // 11
+ // Current audio codec is G723
+ public static final int AUDIO_CODEC_G723 = ImsStreamMediaProfile.AUDIO_QUALITY_G723; // 12
+ // Current audio codec is G711A
+ public static final int AUDIO_CODEC_G711A = ImsStreamMediaProfile.AUDIO_QUALITY_G711A; // 13
+ // Current audio codec is G722
+ public static final int AUDIO_CODEC_G722 = ImsStreamMediaProfile.AUDIO_QUALITY_G722; // 14
+ // Current audio codec is G711AB
+ public static final int AUDIO_CODEC_G711AB = ImsStreamMediaProfile.AUDIO_QUALITY_G711AB; // 15
+ // Current audio codec is G729
+ public static final int AUDIO_CODEC_G729 = ImsStreamMediaProfile.AUDIO_QUALITY_G729; // 16
+ // Current audio codec is EVS_NB
+ public static final int AUDIO_CODEC_EVS_NB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB; // 17
+ // Current audio codec is EVS_WB
+ public static final int AUDIO_CODEC_EVS_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB; // 18
+ // Current audio codec is EVS_SWB
+ public static final int AUDIO_CODEC_EVS_SWB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB; // 19
+ // Current audio codec is EVS_FB
+ public static final int AUDIO_CODEC_EVS_FB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB; // 20
+
+ /**
* Connection extra key used to store the last forwarded number associated with the current
* connection. Used to communicate to the user interface that the connection was forwarded via
* the specified number.
@@ -567,6 +640,13 @@
"android.telecom.extra.IS_RTT_AUDIO_PRESENT";
/**
+ * The audio codec in use for the current {@link Connection}, if known. Valid values include
+ * {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
+ */
+ public static final String EXTRA_AUDIO_CODEC =
+ "android.telecom.extra.AUDIO_CODEC";
+
+ /**
* Connection event used to inform Telecom that it should play the on hold tone. This is used
* to play a tone when the peer puts the current call on hold. Sent to Telecom via
* {@link #sendConnectionEvent(String, Bundle)}.
@@ -1803,6 +1883,12 @@
private Set<String> mPreviousExtraKeys;
/**
+ * The verification status for an incoming call's phone number.
+ */
+ private @VerificationStatus int mCallerNumberVerificationStatus;
+
+
+ /**
* Create a new Connection.
*/
public Connection() {}
@@ -3304,4 +3390,26 @@
public void setCallDirection(@Call.Details.CallDirection int callDirection) {
mCallDirection = callDirection;
}
+
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
+ /**
+ * Sets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * <p>
+ * This property can only be set at the time of creation of a {@link Connection} being returned
+ * by
+ * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ */
+ public void setCallerNumberVerificationStatus(
+ @VerificationStatus int callerNumberVerificationStatus) {
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 812b805..3a0494e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1621,7 +1621,8 @@
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
- connection.getExtras()));
+ connection.getExtras(),
+ connection.getCallerNumberVerificationStatus()));
if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) {
// Tell ConnectionService to show its incoming call UX.
@@ -2156,7 +2157,8 @@
emptyList,
connection.getExtras(),
conferenceId,
- connection.getCallDirection());
+ connection.getCallDirection(),
+ Connection.VERIFICATION_STATUS_NOT_VERIFIED);
mAdapter.addExistingConnection(id, parcelableConnection);
}
}
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index fdc3243..a234bb0 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -66,6 +65,7 @@
private final Bundle mExtras;
private final long mCreationTimeMillis;
private final int mCallDirection;
+ private final int mCallerNumberVerificationStatus;
public ParcelableCall(
String id,
@@ -94,7 +94,8 @@
Bundle intentExtras,
Bundle extras,
long creationTimeMillis,
- int callDirection) {
+ int callDirection,
+ int callerNumberVerificationStatus) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -122,6 +123,7 @@
mExtras = extras;
mCreationTimeMillis = creationTimeMillis;
mCallDirection = callDirection;
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
}
/** The unique ID of the call. */
@@ -322,6 +324,15 @@
return mCallDirection;
}
+ /**
+ * Gets the verification status for the phone number of an incoming call as identified in
+ * ATIS-1000082.
+ * @return the verification status.
+ */
+ public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final @android.annotation.NonNull Parcelable.Creator<ParcelableCall> CREATOR =
@@ -360,6 +371,7 @@
ParcelableRttCall rttCall = source.readParcelable(classLoader);
long creationTimeMillis = source.readLong();
int callDirection = source.readInt();
+ int callerNumberVerificationStatus = source.readInt();
return new ParcelableCall(
id,
state,
@@ -387,7 +399,8 @@
intentExtras,
extras,
creationTimeMillis,
- callDirection);
+ callDirection,
+ callerNumberVerificationStatus);
}
@Override
@@ -433,6 +446,7 @@
destination.writeParcelable(mRttCall, 0);
destination.writeLong(mCreationTimeMillis);
destination.writeInt(mCallDirection);
+ destination.writeInt(mCallerNumberVerificationStatus);
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 4734af6..2b9ce9b 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -54,6 +54,7 @@
private final Bundle mExtras;
private String mParentCallId;
private @Call.Details.CallDirection int mCallDirection;
+ private @Connection.VerificationStatus int mCallerNumberVerificationStatus;
/** @hide */
public ParcelableConnection(
@@ -77,12 +78,13 @@
List<String> conferenceableConnectionIds,
Bundle extras,
String parentCallId,
- @Call.Details.CallDirection int callDirection) {
+ @Call.Details.CallDirection int callDirection,
+ @Connection.VerificationStatus int callerNumberVerificationStatus) {
this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address,
addressPresentation, callerDisplayName, callerDisplayNamePresentation,
videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis,
connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds,
- extras);
+ extras, callerNumberVerificationStatus);
mParentCallId = parentCallId;
mCallDirection = callDirection;
}
@@ -107,7 +109,8 @@
StatusHints statusHints,
DisconnectCause disconnectCause,
List<String> conferenceableConnectionIds,
- Bundle extras) {
+ Bundle extras,
+ @Connection.VerificationStatus int callerNumberVerificationStatus) {
mPhoneAccount = phoneAccount;
mState = state;
mConnectionCapabilities = capabilities;
@@ -129,6 +132,7 @@
mExtras = extras;
mParentCallId = null;
mCallDirection = Call.Details.DIRECTION_UNKNOWN;
+ mCallerNumberVerificationStatus = callerNumberVerificationStatus;
}
public PhoneAccountHandle getPhoneAccount() {
@@ -227,6 +231,10 @@
return mCallDirection;
}
+ public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+ return mCallerNumberVerificationStatus;
+ }
+
@Override
public String toString() {
return new StringBuilder()
@@ -276,6 +284,7 @@
String parentCallId = source.readString();
long connectElapsedTimeMillis = source.readLong();
int callDirection = source.readInt();
+ int callerNumberVerificationStatus = source.readInt();
return new ParcelableConnection(
phoneAccount,
@@ -298,7 +307,8 @@
conferenceableConnectionIds,
extras,
parentCallId,
- callDirection);
+ callDirection,
+ callerNumberVerificationStatus);
}
@Override
@@ -338,5 +348,6 @@
destination.writeString(mParentCallId);
destination.writeLong(mConnectElapsedTimeMillis);
destination.writeInt(mCallDirection);
+ destination.writeInt(mCallerNumberVerificationStatus);
}
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index de3a816..204c37e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -314,8 +314,6 @@
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
- void setTestAutoModeApp(String packageName);
-
/**
* @see TelecomServiceImpl#setSystemDialer
*/
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
similarity index 91%
rename from telephony/java/com/android/internal/telephony/TelephonyPermissions.java
rename to telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 96ddf22..86630b0 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -156,6 +156,27 @@
return false;
}
+ /**
+ * Check whether the app with the given pid/uid can read phone state.
+ *
+ * <p>This method behaves in one of the following ways:
+ * <ul>
+ * <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+ * READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
+ * <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+ * apps which support runtime permissions, if the caller does not currently have any of
+ * these permissions.
+ * <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+ * permissions. This implies that the user revoked the ability to read phone state
+ * manually (via AppOps). In this case we can't throw as it would break app compatibility,
+ * so we return false to indicate that the calling function should return dummy data.
+ * </ul>
+ *
+ * <p>Note: for simplicity, this method always returns false for callers using legacy
+ * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+ * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+ * devices.
+ */
@VisibleForTesting
public static boolean checkReadPhoneState(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -208,6 +229,20 @@
callingPackage, callingFeatureId, message);
}
+ /**
+ * Check whether the app with the given pid/uid can read phone state, or has carrier
+ * privileges on any active subscription.
+ *
+ * <p>If the app does not have carrier privilege, this method will return {@code false} instead
+ * of throwing a SecurityException. Therefore, the callers cannot tell the difference
+ * between M+ apps which declare the runtime permission but do not have it, and pre-M apps
+ * which declare the static permission but had access revoked via AppOps. Apps in the former
+ * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for
+ * use only if the behavior in both scenarios is meant to be identical.
+ *
+ * @return {@code true} if the app can read phone state or has carrier privilege;
+ * {@code false} otherwise.
+ */
@VisibleForTesting
public static boolean checkReadPhoneStateOnAnyActiveSub(
Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid,
@@ -453,6 +488,11 @@
context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingPackageName);
}
+ /**
+ * Check whether the app with the given pid/uid can read the call log.
+ * @return {@code true} if the specified app has the read call log permission and AppOpp granted
+ * to it, {@code false} otherwise.
+ */
@VisibleForTesting
public static boolean checkReadCallLog(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -490,6 +530,12 @@
callingPackage, callingFeatureId, message);
}
+ /**
+ * Returns whether the caller can read phone numbers.
+ *
+ * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
+ * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+ */
@VisibleForTesting
public static boolean checkReadPhoneNumber(
Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -529,10 +575,10 @@
} catch (SecurityException readPhoneNumberSecurityException) {
}
- throw new SecurityException(message + ": Neither user " + uid +
- " nor current process has " + android.Manifest.permission.READ_PHONE_STATE +
- ", " + android.Manifest.permission.READ_SMS + ", or " +
- android.Manifest.permission.READ_PHONE_NUMBERS);
+ throw new SecurityException(message + ": Neither user " + uid
+ + " nor current process has " + android.Manifest.permission.READ_PHONE_STATE
+ + ", " + android.Manifest.permission.READ_SMS + ", or "
+ + android.Manifest.permission.READ_PHONE_NUMBERS);
}
/**
@@ -543,8 +589,8 @@
*/
public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
Context context, int subId, String message) {
- if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
- PERMISSION_GRANTED) {
+ if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PERMISSION_GRANTED) {
return;
}
@@ -586,8 +632,8 @@
}
if (DBG) {
- Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, " +
- "check carrier privilege next.");
+ Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
+ + "check carrier privilege next.");
}
enforceCallingOrSelfCarrierPrivilege(subId, message);
@@ -612,8 +658,8 @@
private static void enforceCarrierPrivilege(
Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) {
- if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) !=
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid)
+ != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
throw new SecurityException(message);
}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 72f758e..f89bbc7 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,6 +1,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.telecom.Connection;
import android.telephony.data.ApnSetting;
import java.lang.annotation.Retention;
@@ -509,4 +510,30 @@
ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
ServiceState.RIL_RADIO_TECHNOLOGY_NR})
public @interface RilRadioTechnology {}
+
+ @IntDef({
+ Connection.AUDIO_CODEC_NONE,
+ Connection.AUDIO_CODEC_AMR,
+ Connection.AUDIO_CODEC_AMR_WB,
+ Connection.AUDIO_CODEC_QCELP13K,
+ Connection.AUDIO_CODEC_EVRC,
+ Connection.AUDIO_CODEC_EVRC_B,
+ Connection.AUDIO_CODEC_EVRC_WB,
+ Connection.AUDIO_CODEC_EVRC_NW,
+ Connection.AUDIO_CODEC_GSM_EFR,
+ Connection.AUDIO_CODEC_GSM_FR,
+ Connection.AUDIO_CODEC_G711U,
+ Connection.AUDIO_CODEC_G723,
+ Connection.AUDIO_CODEC_G711A,
+ Connection.AUDIO_CODEC_G722,
+ Connection.AUDIO_CODEC_G711AB,
+ Connection.AUDIO_CODEC_G729,
+ Connection.AUDIO_CODEC_EVS_NB,
+ Connection.AUDIO_CODEC_EVS_WB,
+ Connection.AUDIO_CODEC_EVS_SWB,
+ Connection.AUDIO_CODEC_EVS_FB
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsAudioCodec {
+ }
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 98d7661..1b86c09 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -100,6 +100,16 @@
KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
/**
+ * Boolean indicating the Supplementary Services(SS) is disable when airplane mode on in the
+ * Call Settings menu.
+ * {@code true}: SS is disable when airplane mode on.
+ * {@code false}: SS is enable when airplane mode on.
+ * The default value for this key is {@code false}
+ */
+ public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL =
+ "disable_supplementary_services_in_airplane_mode_bool";
+
+ /**
* Boolean indicating if the "Call forwarding" item is visible in the Call Settings menu.
* true means visible. false means gone.
* @hide
@@ -3439,6 +3449,7 @@
sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
+ sDefaults.putBoolean(KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 17b7963f..8a75831 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -16,6 +16,8 @@
package android.telephony;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,12 +29,13 @@
/**
* Define capability of a modem group. That is, the capabilities
* are shared between those modems defined by list of modem IDs.
- * @hide
*/
-public class PhoneCapability implements Parcelable {
+public final class PhoneCapability implements Parcelable {
// Hardcoded default DSDS capability.
+ /** @hide */
public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
// Hardcoded default Single SIM single standby capability.
+ /** @hide */
public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
static {
@@ -48,13 +51,18 @@
logicalModemList.add(modemInfo1);
DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
}
-
+ /** @hide */
public final int maxActiveVoiceCalls;
+ /** @hide */
public final int maxActiveData;
+ /** @hide */
public final int max5G;
+ /** @hide */
public final boolean validationBeforeSwitchSupported;
+ /** @hide */
public final List<ModemInfo> logicalModemList;
+ /** @hide */
public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
this.maxActiveVoiceCalls = maxActiveVoiceCalls;
@@ -116,7 +124,7 @@
/**
* {@link Parcelable#writeToParcel}
*/
- public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+ public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
dest.writeInt(maxActiveVoiceCalls);
dest.writeInt(maxActiveData);
dest.writeInt(max5G);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9ace86c..d2c8517 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -278,12 +278,9 @@
*/
public static final int UNKNOWN_ID = -1;
- private String mVoiceOperatorAlphaLong;
- private String mVoiceOperatorAlphaShort;
- private String mVoiceOperatorNumeric;
- private String mDataOperatorAlphaLong;
- private String mDataOperatorAlphaShort;
- private String mDataOperatorNumeric;
+ private String mOperatorAlphaLong;
+ private String mOperatorAlphaShort;
+ private String mOperatorNumeric;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private boolean mIsManualNetworkSelection;
@@ -379,12 +376,9 @@
protected void copyFrom(ServiceState s) {
mVoiceRegState = s.mVoiceRegState;
mDataRegState = s.mDataRegState;
- mVoiceOperatorAlphaLong = s.mVoiceOperatorAlphaLong;
- mVoiceOperatorAlphaShort = s.mVoiceOperatorAlphaShort;
- mVoiceOperatorNumeric = s.mVoiceOperatorNumeric;
- mDataOperatorAlphaLong = s.mDataOperatorAlphaLong;
- mDataOperatorAlphaShort = s.mDataOperatorAlphaShort;
- mDataOperatorNumeric = s.mDataOperatorNumeric;
+ mOperatorAlphaLong = s.mOperatorAlphaLong;
+ mOperatorAlphaShort = s.mOperatorAlphaShort;
+ mOperatorNumeric = s.mOperatorNumeric;
mIsManualNetworkSelection = s.mIsManualNetworkSelection;
mCssIndicator = s.mCssIndicator;
mNetworkId = s.mNetworkId;
@@ -418,12 +412,9 @@
public ServiceState(Parcel in) {
mVoiceRegState = in.readInt();
mDataRegState = in.readInt();
- mVoiceOperatorAlphaLong = in.readString();
- mVoiceOperatorAlphaShort = in.readString();
- mVoiceOperatorNumeric = in.readString();
- mDataOperatorAlphaLong = in.readString();
- mDataOperatorAlphaShort = in.readString();
- mDataOperatorNumeric = in.readString();
+ mOperatorAlphaLong = in.readString();
+ mOperatorAlphaShort = in.readString();
+ mOperatorNumeric = in.readString();
mIsManualNetworkSelection = in.readInt() != 0;
mCssIndicator = (in.readInt() != 0);
mNetworkId = in.readInt();
@@ -448,12 +439,9 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mVoiceRegState);
out.writeInt(mDataRegState);
- out.writeString(mVoiceOperatorAlphaLong);
- out.writeString(mVoiceOperatorAlphaShort);
- out.writeString(mVoiceOperatorNumeric);
- out.writeString(mDataOperatorAlphaLong);
- out.writeString(mDataOperatorAlphaShort);
- out.writeString(mDataOperatorNumeric);
+ out.writeString(mOperatorAlphaLong);
+ out.writeString(mOperatorAlphaShort);
+ out.writeString(mOperatorNumeric);
out.writeInt(mIsManualNetworkSelection ? 1 : 0);
out.writeInt(mCssIndicator ? 1 : 0);
out.writeInt(mNetworkId);
@@ -691,7 +679,7 @@
* @return long name of operator, null if unregistered or unknown
*/
public String getOperatorAlphaLong() {
- return mVoiceOperatorAlphaLong;
+ return mOperatorAlphaLong;
}
/**
@@ -699,18 +687,10 @@
* @return long name of operator
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@link #getOperatorAlphaLong} instead.")
public String getVoiceOperatorAlphaLong() {
- return mVoiceOperatorAlphaLong;
- }
-
- /**
- * Get current registered data network operator name in long alphanumeric format.
- * @return long name of voice operator
- * @hide
- */
- public String getDataOperatorAlphaLong() {
- return mDataOperatorAlphaLong;
+ return mOperatorAlphaLong;
}
/**
@@ -721,7 +701,7 @@
* @return short name of operator, null if unregistered or unknown
*/
public String getOperatorAlphaShort() {
- return mVoiceOperatorAlphaShort;
+ return mOperatorAlphaShort;
}
/**
@@ -729,9 +709,10 @@
* @return short name of operator, null if unregistered or unknown
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
public String getVoiceOperatorAlphaShort() {
- return mVoiceOperatorAlphaShort;
+ return mOperatorAlphaShort;
}
/**
@@ -739,9 +720,10 @@
* @return short name of operator, null if unregistered or unknown
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
public String getDataOperatorAlphaShort() {
- return mDataOperatorAlphaShort;
+ return mOperatorAlphaShort;
}
/**
@@ -755,11 +737,11 @@
* @hide
*/
public String getOperatorAlpha() {
- if (TextUtils.isEmpty(mVoiceOperatorAlphaLong)) {
- return mVoiceOperatorAlphaShort;
+ if (TextUtils.isEmpty(mOperatorAlphaLong)) {
+ return mOperatorAlphaShort;
}
- return mVoiceOperatorAlphaLong;
+ return mOperatorAlphaLong;
}
/**
@@ -775,7 +757,7 @@
* {@link com.android.internal.telephony.MccTable#countryCodeForMcc(int)}.
*/
public String getOperatorNumeric() {
- return mVoiceOperatorNumeric;
+ return mOperatorNumeric;
}
/**
@@ -785,7 +767,7 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public String getVoiceOperatorNumeric() {
- return mVoiceOperatorNumeric;
+ return mOperatorNumeric;
}
/**
@@ -793,9 +775,10 @@
* @return numeric format of operator, null if unregistered or unknown
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+ publicAlternatives = "Use {@link #getOperatorNumeric} instead.")
public String getDataOperatorNumeric() {
- return mDataOperatorNumeric;
+ return mOperatorNumeric;
}
/**
@@ -815,12 +798,9 @@
mDataRegState,
mChannelNumber,
Arrays.hashCode(mCellBandwidths),
- mVoiceOperatorAlphaLong,
- mVoiceOperatorAlphaShort,
- mVoiceOperatorNumeric,
- mDataOperatorAlphaLong,
- mDataOperatorAlphaShort,
- mDataOperatorNumeric,
+ mOperatorAlphaLong,
+ mOperatorAlphaShort,
+ mOperatorNumeric,
mIsManualNetworkSelection,
mCssIndicator,
mNetworkId,
@@ -850,12 +830,9 @@
&& mIsManualNetworkSelection == s.mIsManualNetworkSelection
&& mChannelNumber == s.mChannelNumber
&& Arrays.equals(mCellBandwidths, s.mCellBandwidths)
- && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
- && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
- && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
- && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
- && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
- && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
+ && equalsHandlesNulls(mOperatorAlphaLong, s.mOperatorAlphaLong)
+ && equalsHandlesNulls(mOperatorAlphaShort, s.mOperatorAlphaShort)
+ && equalsHandlesNulls(mOperatorNumeric, s.mOperatorNumeric)
&& equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
&& equalsHandlesNulls(mNetworkId, s.mNetworkId)
&& equalsHandlesNulls(mSystemId, s.mSystemId)
@@ -1007,10 +984,8 @@
.append(", mChannelNumber=").append(mChannelNumber)
.append(", duplexMode()=").append(getDuplexMode())
.append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
- .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
- .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
- .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
- .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+ .append(", mOperatorAlphaLong=").append(mOperatorAlphaLong)
+ .append(", mOperatorAlphaShort=").append(mOperatorAlphaShort)
.append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
.append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
.append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
@@ -1040,12 +1015,9 @@
mDataRegState = STATE_OUT_OF_SERVICE;
mChannelNumber = -1;
mCellBandwidths = new int[0];
- mVoiceOperatorAlphaLong = null;
- mVoiceOperatorAlphaShort = null;
- mVoiceOperatorNumeric = null;
- mDataOperatorAlphaLong = null;
- mDataOperatorAlphaShort = null;
- mDataOperatorNumeric = null;
+ mOperatorAlphaLong = null;
+ mOperatorAlphaShort = null;
+ mOperatorNumeric = null;
mIsManualNetworkSelection = false;
mCssIndicator = false;
mNetworkId = -1;
@@ -1204,26 +1176,9 @@
}
public void setOperatorName(String longName, String shortName, String numeric) {
- mVoiceOperatorAlphaLong = longName;
- mVoiceOperatorAlphaShort = shortName;
- mVoiceOperatorNumeric = numeric;
- mDataOperatorAlphaLong = longName;
- mDataOperatorAlphaShort = shortName;
- mDataOperatorNumeric = numeric;
- }
-
- /** @hide */
- public void setVoiceOperatorName(String longName, String shortName, String numeric) {
- mVoiceOperatorAlphaLong = longName;
- mVoiceOperatorAlphaShort = shortName;
- mVoiceOperatorNumeric = numeric;
- }
-
- /** @hide */
- public void setDataOperatorName(String longName, String shortName, String numeric) {
- mDataOperatorAlphaLong = longName;
- mDataOperatorAlphaShort = shortName;
- mDataOperatorNumeric = numeric;
+ mOperatorAlphaLong = longName;
+ mOperatorAlphaShort = shortName;
+ mOperatorNumeric = numeric;
}
/**
@@ -1233,19 +1188,8 @@
* @hide
*/
@UnsupportedAppUsage
- public void setOperatorAlphaLong(String longName) {
- mVoiceOperatorAlphaLong = longName;
- mDataOperatorAlphaLong = longName;
- }
-
- /** @hide */
- public void setVoiceOperatorAlphaLong(String longName) {
- mVoiceOperatorAlphaLong = longName;
- }
-
- /** @hide */
- public void setDataOperatorAlphaLong(String longName) {
- mDataOperatorAlphaLong = longName;
+ public void setOperatorAlphaLong(@Nullable String longName) {
+ mOperatorAlphaLong = longName;
}
public void setIsManualSelection(boolean isManual) {
@@ -1293,12 +1237,12 @@
m.putInt("dataRegState", mDataRegState);
m.putInt("dataRoamingType", getDataRoamingType());
m.putInt("voiceRoamingType", getVoiceRoamingType());
- m.putString("operator-alpha-long", mVoiceOperatorAlphaLong);
- m.putString("operator-alpha-short", mVoiceOperatorAlphaShort);
- m.putString("operator-numeric", mVoiceOperatorNumeric);
- m.putString("data-operator-alpha-long", mDataOperatorAlphaLong);
- m.putString("data-operator-alpha-short", mDataOperatorAlphaShort);
- m.putString("data-operator-numeric", mDataOperatorNumeric);
+ m.putString("operator-alpha-long", mOperatorAlphaLong);
+ m.putString("operator-alpha-short", mOperatorAlphaShort);
+ m.putString("operator-numeric", mOperatorNumeric);
+ m.putString("data-operator-alpha-long", mOperatorAlphaLong);
+ m.putString("data-operator-alpha-short", mOperatorAlphaShort);
+ m.putString("data-operator-numeric", mOperatorNumeric);
m.putBoolean("manual", mIsManualNetworkSelection);
m.putInt("radioTechnology", getRilVoiceRadioTechnology());
m.putInt("dataRadioTechnology", getRadioTechnology());
@@ -1933,12 +1877,9 @@
}
if (!removeCoarseLocation) return state;
- state.mDataOperatorAlphaLong = null;
- state.mDataOperatorAlphaShort = null;
- state.mDataOperatorNumeric = null;
- state.mVoiceOperatorAlphaLong = null;
- state.mVoiceOperatorAlphaShort = null;
- state.mVoiceOperatorNumeric = null;
+ state.mOperatorAlphaLong = null;
+ state.mOperatorAlphaShort = null;
+ state.mOperatorNumeric = null;
return state;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fc26122..e3981f6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4723,17 +4723,6 @@
}
/**
- * Sim activation type: voice
- * @hide
- */
- public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
- /**
- * Sim activation type: data
- * @hide
- */
- public static final int SIM_ACTIVATION_TYPE_DATA = 1;
-
- /**
* Initial SIM activation state, unknown. Not set by any carrier apps.
* @hide
*/
@@ -8172,17 +8161,25 @@
return Collections.EMPTY_LIST;
}
- /** @hide */
- public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+ /**
+ * Get the names of packages with carrier privileges for all the active subscriptions.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @NonNull
+ public List<String> getCarrierPrivilegedPackagesForAllActiveSubscriptions() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony.getPackagesWithCarrierPrivilegesForAllPhones();
}
} catch (RemoteException ex) {
- Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones RemoteException", ex);
+ Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions RemoteException",
+ ex);
} catch (NullPointerException ex) {
- Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones NPE", ex);
+ Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions NPE", ex);
}
return Collections.EMPTY_LIST;
}
diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tests/ManagedProfileLifecycleStressTest/Android.bp
similarity index 73%
rename from tools/processors/unsupportedappusage/test/Android.bp
rename to tests/ManagedProfileLifecycleStressTest/Android.bp
index 49ea3d4..639ce3c 100644
--- a/tools/processors/unsupportedappusage/test/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/Android.bp
@@ -13,16 +13,11 @@
// limitations under the License.
java_test_host {
- name: "unsupportedappusage-processor-test",
-
+ name: "ManagedProfileLifecycleStressTest",
srcs: ["src/**/*.java"],
-
- static_libs: [
- "libjavac",
- "unsupportedappusage-annotation-processor-lib",
- "truth-host-prebuilt",
- "mockito-host",
- "junit-host",
- "objenesis",
+ libs: ["tradefed"],
+ test_suites: ["device-tests"],
+ target_required: [
+ "DummyDPC",
],
}
diff --git a/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml
new file mode 100644
index 0000000..e7dbc51
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<configuration description="Stress test for managed profile lifecycle">
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="ManagedProfileLifecycleStressTest.jar" />
+ </test>
+</configuration>
diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
similarity index 70%
copy from tools/processors/unsupportedappusage/test/Android.bp
copy to tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
index 49ea3d4..d95af34 100644
--- a/tools/processors/unsupportedappusage/test/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
@@ -12,17 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-java_test_host {
- name: "unsupportedappusage-processor-test",
-
+android_test {
+ name: "DummyDPC",
+ defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
-
- static_libs: [
- "libjavac",
- "unsupportedappusage-annotation-processor-lib",
- "truth-host-prebuilt",
- "mockito-host",
- "junit-host",
- "objenesis",
- ],
+ sdk_version: "current",
}
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml
new file mode 100644
index 0000000..860940d
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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.dummydpc">
+
+ <application
+ android:testOnly="true">
+ <receiver
+ android:name="com.android.dummydpc.DummyDeviceAdminReceiver"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml
new file mode 100644
index 0000000..4b3581e
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml
@@ -0,0 +1,4 @@
+<device-admin>
+ <uses-policies>
+ </uses-policies>
+</device-admin>
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
similarity index 62%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
index 007ec94..92190b7 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -13,7 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.dummydpc;
-package android.net.wifi;
+import android.app.admin.DeviceAdminReceiver;
-parcelable WifiActivityEnergyInfo;
+/**
+ * Empty admin to use as a managed profile owner.
+ */
+public class DummyDeviceAdminReceiver extends DeviceAdminReceiver {
+}
+
diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
new file mode 100644
index 0000000..e323592
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.test.stress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A test to exercise Android Framework parts related to creating, starting, stopping, and deleting
+ * a managed profile as much as possible. The aim is to catch any issues in this code before it
+ * affects managed profile CTS tests.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ManagedProfileLifecycleStressTest extends BaseHostJUnit4Test {
+ // Stop the test once this time limit has been reached. 25 minutes used as a limit to make total
+ // test time less than 30 minutes, so that it can be put into presubmit.
+ private static final int TIME_LIMIT_MINUTES = 25;
+
+ private static final String DUMMY_DPC_APK = "DummyDPC.apk";
+ private static final String DUMMY_DPC_COMPONENT =
+ "com.android.dummydpc/com.android.dummydpc.DummyDeviceAdminReceiver";
+ private static final Pattern CREATE_USER_OUTPUT_REGEX =
+ Pattern.compile("Success: created user id (\\d+)");
+
+ /**
+ * Create, start, and kill managed profiles in a loop.
+ */
+ @Test
+ public void testCreateStartDelete() throws Exception {
+ int iteration = 0;
+ final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES);
+ while (System.nanoTime() < deadline) {
+ iteration++;
+ CLog.w("Iteration N" + iteration);
+ final int userId = createManagedProfile();
+ startUser(userId);
+ installPackageAsUser(DUMMY_DPC_APK, true /* grantPermissions */, userId, "-t");
+ setProfileOwner(DUMMY_DPC_COMPONENT, userId);
+ removeUser(userId);
+ }
+ CLog.w("Completed " + iteration + " iterations.");
+ }
+
+ private int createManagedProfile() throws Exception {
+ final String output = getDevice().executeShellCommand(
+ "pm create-user --profileOf 0 --managed TestProfile");
+ final Matcher matcher = CREATE_USER_OUTPUT_REGEX.matcher(output.trim());
+ if (!matcher.matches() || matcher.groupCount() != 1) {
+ fail("user creation failed, output: " + output);
+ }
+ return Integer.parseInt(matcher.group(1));
+ }
+
+ private void setProfileOwner(String componentName, int userId) throws Exception {
+ String command = "dpm set-profile-owner --user " + userId + " '" + componentName + "'";
+ String commandOutput = getDevice().executeShellCommand(command);
+ assertTrue("Unexpected dpm output: " + commandOutput, commandOutput.startsWith("Success:"));
+ }
+
+ private void removeUser(int userId) throws Exception {
+ final String output = getDevice().executeShellCommand("pm remove-user " + userId).trim();
+ assertEquals("Unexpected pm output: " + output, "Success: removed user", output);
+ }
+
+ private void startUser(int userId) throws Exception {
+ final String output = getDevice().executeShellCommand("am start-user -w " + userId).trim();
+ assertEquals("Unexpected am output: " + output, "Success: user started", output);
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fd3ed7d..a24426b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -3134,14 +3134,11 @@
.addTransportType(TRANSPORT_CELLULAR).build();
final TestNetworkCallback cellCallback = new TestNetworkCallback();
mCm.requestNetwork(cellRequest, cellCallback);
- // NOTE: This request causes the network's capabilities to change. This
- // is currently delivered before the onAvailable() callbacks.
- // TODO: Fix this.
- cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
// Expect a network capabilities update with FOREGROUND, because the most recent
// request causes its state to change.
+ cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp
deleted file mode 100644
index 1e96234..0000000
--- a/tools/processors/unsupportedappusage/Android.bp
+++ /dev/null
@@ -1,34 +0,0 @@
-
-java_library_host {
- name: "unsupportedappusage-annotation-processor-lib",
- srcs: [
- "src/**/*.java",
- ],
- static_libs: [
- "guava",
- "unsupportedappusage-annotation"
- ],
- openjdk9: {
- javacflags: [
- "--add-modules=jdk.compiler",
- "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
- ],
- },
-}
-
-java_plugin {
- name: "unsupportedappusage-annotation-processor",
- processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor",
-
- java_resources: [
- "META-INF/**/*",
- ],
- static_libs: [
- "unsupportedappusage-annotation-processor-lib"
- ],
-
- use_tools_jar: true,
-}
diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
deleted file mode 100644
index 4a969d3..0000000
--- a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
+++ /dev/null
@@ -1 +0,0 @@
-android.processor.unsupportedappusage.UnsupportedAppUsageProcessor
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
deleted file mode 100644
index 65fc733..0000000
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2018 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.processor.unsupportedappusage;
-
-import static javax.lang.model.element.ElementKind.PACKAGE;
-import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.sun.tools.javac.code.Type;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Builds a dex signature for a given method or field.
- */
-public class SignatureBuilder {
-
- private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder()
- .put(TypeKind.BOOLEAN, "Z")
- .put(TypeKind.BYTE, "B")
- .put(TypeKind.CHAR, "C")
- .put(TypeKind.DOUBLE, "D")
- .put(TypeKind.FLOAT, "F")
- .put(TypeKind.INT, "I")
- .put(TypeKind.LONG, "J")
- .put(TypeKind.SHORT, "S")
- .put(TypeKind.VOID, "V")
- .build();
-
- private final Messager mMessager;
-
- /**
- * Exception used internally when we can't build a signature. Whenever this is thrown, an error
- * will also be written to the Messager.
- */
- private class SignatureBuilderException extends Exception {
- public SignatureBuilderException(String message) {
- super(message);
- }
-
- public void report(Element offendingElement) {
- mMessager.printMessage(ERROR, getMessage(), offendingElement);
- }
- }
-
- public SignatureBuilder(Messager messager) {
- mMessager = messager;
- }
-
- /**
- * Returns a list of enclosing elements for the given element, with the package first, and
- * excluding the element itself.
- */
- private List<Element> getEnclosingElements(Element e) {
- List<Element> enclosing = new ArrayList<>();
- e = e.getEnclosingElement(); // don't include the element itself.
- while (e != null) {
- enclosing.add(e);
- e = e.getEnclosingElement();
- }
- Collections.reverse(enclosing);
- return enclosing;
- }
-
- /**
- * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;"
- */
- private String getClassSignature(TypeElement clazz) {
- StringBuilder sb = new StringBuilder("L");
- for (Element enclosing : getEnclosingElements(clazz)) {
- switch (enclosing.getKind()) {
- case MODULE:
- // ignore this.
- break;
- case PACKAGE:
- sb.append(((PackageElement) enclosing)
- .getQualifiedName()
- .toString()
- .replace('.', '/'));
- sb.append('/');
- break;
- default:
- sb.append(enclosing.getSimpleName()).append('$');
- break;
- }
-
- }
- return sb
- .append(clazz.getSimpleName())
- .append(";")
- .toString();
- }
-
- /**
- * Returns the type signature for a given type. For primitive types, a single character.
- * For classes, the class signature. For arrays, a "[" preceeding the component type.
- */
- private String getTypeSignature(TypeMirror type) throws SignatureBuilderException {
- String sig = TYPE_MAP.get(type.getKind());
- if (sig != null) {
- return sig;
- }
- switch (type.getKind()) {
- case ARRAY:
- return "[" + getTypeSignature(((ArrayType) type).getComponentType());
- case DECLARED:
- Element declaring = ((DeclaredType) type).asElement();
- if (!(declaring instanceof TypeElement)) {
- throw new SignatureBuilderException(
- "Can't handle declared type of kind " + declaring.getKind());
- }
- return getClassSignature((TypeElement) declaring);
- case TYPEVAR:
- Type.TypeVar typeVar = (Type.TypeVar) type;
- if (typeVar.getLowerBound().getKind() != TypeKind.NULL) {
- return getTypeSignature(typeVar.getLowerBound());
- } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) {
- return getTypeSignature(typeVar.getUpperBound());
- } else {
- throw new SignatureBuilderException("Can't handle typevar with no bound");
- }
-
- default:
- throw new SignatureBuilderException("Can't handle type of kind " + type.getKind());
- }
- }
-
- /**
- * Get the signature for an executable, either a method or a constructor.
- *
- * @param name "<init>" for constructor, else the method name
- * @param method The executable element in question.
- */
- private String getExecutableSignature(CharSequence name, ExecutableElement method)
- throws SignatureBuilderException {
- StringBuilder sig = new StringBuilder();
- sig.append(getClassSignature((TypeElement) method.getEnclosingElement()))
- .append("->")
- .append(name)
- .append("(");
- for (VariableElement param : method.getParameters()) {
- sig.append(getTypeSignature(param.asType()));
- }
- sig.append(")")
- .append(getTypeSignature(method.getReturnType()));
- return sig.toString();
- }
-
- private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException {
- return getExecutableSignature(method.getSimpleName(), method);
- }
-
- private String buildConstructorSignature(ExecutableElement cons)
- throws SignatureBuilderException {
- return getExecutableSignature("<init>", cons);
- }
-
- private String buildFieldSignature(VariableElement field) throws SignatureBuilderException {
- StringBuilder sig = new StringBuilder();
- sig.append(getClassSignature((TypeElement) field.getEnclosingElement()))
- .append("->")
- .append(field.getSimpleName())
- .append(":")
- .append(getTypeSignature(field.asType()))
- ;
- return sig.toString();
- }
-
- /**
- * Creates the signature for an annotated element.
- *
- * @param annotationType type of annotation being processed.
- * @param element element for which we want to create a signature.
- */
- public String buildSignature(Class<? extends Annotation> annotationType, Element element) {
- try {
- String signature;
- switch (element.getKind()) {
- case METHOD:
- signature = buildMethodSignature((ExecutableElement) element);
- break;
- case CONSTRUCTOR:
- signature = buildConstructorSignature((ExecutableElement) element);
- break;
- case FIELD:
- signature = buildFieldSignature((VariableElement) element);
- break;
- default:
- return null;
- }
- // Obtain annotation objects
- Annotation annotation = element.getAnnotation(annotationType);
- if (annotation == null) {
- throw new IllegalStateException(
- "Element doesn't have any UnsupportedAppUsage annotation");
- }
- try {
- Method expectedSignatureMethod = annotationType.getMethod("expectedSignature");
- // If we have an expected signature on the annotation, warn if it doesn't match.
- String expectedSignature = expectedSignatureMethod.invoke(annotation).toString();
- if (!Strings.isNullOrEmpty(expectedSignature)) {
- if (!signature.equals(expectedSignature)) {
- mMessager.printMessage(
- WARNING,
- String.format(
- "Expected signature doesn't match generated signature.\n"
- + " Expected: %s\n Generated: %s",
- expectedSignature, signature),
- element);
- }
- }
- return signature;
- } catch (NoSuchMethodException e) {
- throw new IllegalStateException(
- "Annotation type does not have expectedSignature parameter", e);
- } catch (IllegalAccessException | InvocationTargetException e) {
- throw new IllegalStateException(
- "Could not get expectedSignature parameter for annotation", e);
- }
- } catch (SignatureBuilderException problem) {
- problem.report(element);
- return null;
- }
- }
-}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
deleted file mode 100644
index 5bb956a..0000000
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2018 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.processor.unsupportedappusage;
-
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import com.sun.tools.javac.model.JavacElements;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.util.Pair;
-import com.sun.tools.javac.util.Position;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.net.URLEncoder;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.stream.Stream;
-
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Annotation processor for {@link UnsupportedAppUsage} annotations.
- *
- * This processor currently outputs a CSV file with a mapping of dex signatures to corresponding
- * source positions.
- *
- * This is used for automating updates to the annotations themselves.
- */
-@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage",
- "dalvik.annotation.compat.UnsupportedAppUsage"
-})
-public class UnsupportedAppUsageProcessor extends AbstractProcessor {
-
- // Package name for writing output. Output will be written to the "class output" location within
- // this package.
- private static final String PACKAGE = "unsupportedappusage";
- private static final String INDEX_CSV = "unsupportedappusage_index.csv";
-
- private static final ImmutableSet<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
- ImmutableSet.of(android.annotation.UnsupportedAppUsage.class,
- dalvik.annotation.compat.UnsupportedAppUsage.class);
- private static final ImmutableSet<String> SUPPORTED_ANNOTATION_NAMES =
- SUPPORTED_ANNOTATIONS.stream().map(annotation -> annotation.getCanonicalName()).collect(
- ImmutableSet.toImmutableSet());
-
- @Override
- public SourceVersion getSupportedSourceVersion() {
- return SourceVersion.latest();
- }
-
- /**
- * Write the contents of a stream to a text file, with one line per item.
- */
- private void writeToFile(String name,
- String headerLine,
- Stream<?> contents) throws IOException {
- PrintStream out = new PrintStream(processingEnv.getFiler().createResource(
- CLASS_OUTPUT,
- PACKAGE,
- name)
- .openOutputStream());
- out.println(headerLine);
- contents.forEach(o -> out.println(o));
- if (out.checkError()) {
- throw new IOException("Error when writing to " + name);
- }
- out.close();
- }
-
- /**
- * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element.
- */
- private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) {
- for (AnnotationMirror m : e.getAnnotationMirrors()) {
- TypeElement type = (TypeElement) m.getAnnotationType().asElement();
- if (SUPPORTED_ANNOTATION_NAMES.contains(type.getQualifiedName().toString())) {
- return m;
- }
- }
- return null;
- }
-
- /**
- * Returns a CSV header line for the columns returned by
- * {@link #getAnnotationIndex(String, Element)}.
- */
- private String getCsvHeaders() {
- return Joiner.on(',').join(
- "signature",
- "file",
- "startline",
- "startcol",
- "endline",
- "endcol",
- "properties"
- );
- }
-
- private String encodeAnnotationProperties(AnnotationMirror annotation) {
- StringBuilder sb = new StringBuilder();
- for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
- : annotation.getElementValues().entrySet()) {
- if (sb.length() > 0) {
- sb.append("&");
- }
- sb.append(e.getKey().getSimpleName())
- .append("=")
- .append(URLEncoder.encode(e.getValue().toString()));
- }
- return sb.toString();
- }
-
- /**
- * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
- * attached to it. It returns CSV in the format:
- * dex-signature,filename,start-line,start-col,end-line,end-col
- *
- * The positions refer to the annotation itself, *not* the annotated member. This can therefore
- * be used to read just the annotation from the file, and to perform in-place edits on it.
- *
- * @param signature the dex signature for the element.
- * @param annotatedElement The annotated element
- * @return A single line of CSV text
- */
- private String getAnnotationIndex(String signature, Element annotatedElement) {
- JavacElements javacElem = (JavacElements) processingEnv.getElementUtils();
- AnnotationMirror unsupportedAppUsage =
- getUnsupportedAppUsageAnnotationMirror(annotatedElement);
- Pair<JCTree, JCTree.JCCompilationUnit> pair =
- javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null);
- Position.LineMap lines = pair.snd.lineMap;
- return Joiner.on(",").join(
- signature,
- pair.snd.getSourceFile().getName(),
- lines.getLineNumber(pair.fst.pos().getStartPosition()),
- lines.getColumnNumber(pair.fst.pos().getStartPosition()),
- lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
- lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
- encodeAnnotationProperties(unsupportedAppUsage));
- }
-
- /**
- * This is the main entry point in the processor, called by the compiler.
- */
- @Override
- public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- Map<String, Element> signatureMap = new TreeMap<>();
- SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager());
- for (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) {
- Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
- supportedAnnotation);
- if (annotated.size() == 0) {
- continue;
- }
- // Build signatures for each annotated member and put them in a map from signature to
- // member.
- for (Element e : annotated) {
- String sig = sb.buildSignature(supportedAnnotation, e);
- if (sig != null) {
- signatureMap.put(sig, e);
- }
- }
- }
-
- if (!signatureMap.isEmpty()) {
- try {
- writeToFile(INDEX_CSV,
- getCsvHeaders(),
- signatureMap.entrySet()
- .stream()
- .map(e -> getAnnotationIndex(e.getKey(), e.getValue())));
- } catch (IOException e) {
- throw new RuntimeException("Failed to write output", e);
- }
- }
- return true;
- }
-}
diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java
deleted file mode 100644
index 23db99e..0000000
--- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.processor.unsupportedappusage;
-
-import com.google.common.base.Splitter;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class CsvReader {
-
- private final Splitter mSplitter;
- private final List<String> mColumns;
- private final List<Map<String, String>> mContents;
-
- public CsvReader(InputStream in) throws IOException {
- mSplitter = Splitter.on(",");
- BufferedReader br = new BufferedReader(new InputStreamReader(in));
- mColumns = mSplitter.splitToList(br.readLine());
- mContents = new ArrayList<>();
- String line = br.readLine();
- while (line != null) {
- List<String> contents = mSplitter.splitToList(line);
- Map<String, String> contentMap = new HashMap<>();
- for (int i = 0; i < Math.min(contents.size(), mColumns.size()); ++i) {
- contentMap.put(mColumns.get(i), contents.get(i));
- }
- mContents.add(contentMap);
- line = br.readLine();
- }
- br.close();
- }
-
- public List<String> getColumns() {
- return mColumns;
- }
-
- public List<Map<String, String>> getContents() {
- return mContents;
- }
-}
diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
deleted file mode 100644
index 012e88f..0000000
--- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.processor.unsupportedappusage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.javac.Javac;
-
-import com.google.common.base.Joiner;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.Map;
-
-public class UnsupportedAppUsageProcessorTest {
-
- private Javac mJavac;
-
- @Before
- public void setup() throws IOException {
- mJavac = new Javac();
- mJavac.addSource("dalvik.annotation.compat.UnsupportedAppUsage", Joiner.on('\n').join(
- "package dalvik.annotation.compat;",
- "public @interface UnsupportedAppUsage {",
- " String expectedSignature() default \"\";\n",
- " String someProperty() default \"\";",
- "}"));
- }
-
- private CsvReader compileAndReadCsv() throws IOException {
- mJavac.compileWithAnnotationProcessor(new UnsupportedAppUsageProcessor());
- return new CsvReader(
- mJavac.getOutputFile("unsupportedappusage/unsupportedappusage_index.csv"));
- }
-
- @Test
- public void testSignatureFormat() throws Exception {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;",
- "import dalvik.annotation.compat.UnsupportedAppUsage;",
- "public class Class {",
- " @UnsupportedAppUsage",
- " public void method() {}",
- "}"));
- assertThat(compileAndReadCsv().getContents().get(0)).containsEntry(
- "signature", "La/b/Class;->method()V"
- );
- }
-
- @Test
- public void testSourcePosition() throws Exception {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;", // 1
- "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2
- "public class Class {", // 3
- " @UnsupportedAppUsage", // 4
- " public void method() {}", // 5
- "}"));
- Map<String, String> row = compileAndReadCsv().getContents().get(0);
- assertThat(row).containsEntry("startline", "4");
- assertThat(row).containsEntry("startcol", "3");
- assertThat(row).containsEntry("endline", "4");
- assertThat(row).containsEntry("endcol", "23");
- }
-
- @Test
- public void testAnnotationProperties() throws Exception {
- mJavac.addSource("a.b.Class", Joiner.on('\n').join(
- "package a.b;", // 1
- "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2
- "public class Class {", // 3
- " @UnsupportedAppUsage(someProperty=\"value\")", // 4
- " public void method() {}", // 5
- "}"));
- assertThat(compileAndReadCsv().getContents().get(0)).containsEntry(
- "properties", "someProperty=%22value%22");
- }
-
-
-}
diff --git a/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl
new file mode 100644
index 0000000..7e25fd8
--- /dev/null
+++ b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl
@@ -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.
+ */
+
+package android.net.wifi;
+
+import android.os.connectivity.WifiActivityEnergyInfo;
+
+/**
+ * Interface for Wi-Fi activity energy info listener.
+ *
+ * @hide
+ */
+oneway interface IOnWifiActivityEnergyInfoListener
+{
+ /**
+ * Service to manager callback providing current Wi-Fi activity energy info.
+ * @param info the Wi-Fi activity energy info
+ */
+ void onWifiActivityEnergyInfo(in WifiActivityEnergyInfo info);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 032f66a..fccbcf7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,15 +28,15 @@
import android.net.wifi.IDppCallback;
import android.net.wifi.ILocalOnlyHotspotCallback;
import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.IOnWifiActivityEnergyInfoListener;
+import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.IScanResultsCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ISuggestionConnectionStatusListener;
import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.ITxPacketCountListener;
-import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiNetworkSuggestion;
@@ -44,6 +44,7 @@
import android.os.Messenger;
import android.os.ResultReceiver;
import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
/**
* Interface that allows controlling and querying Wi-Fi connectivity.
@@ -56,13 +57,7 @@
WifiActivityEnergyInfo reportActivityInfo();
- /**
- * Requests the controller activity info asynchronously.
- * The implementor is expected to reply with the
- * {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key
- * {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored.
- */
- oneway void requestActivityInfo(in ResultReceiver result);
+ oneway void getWifiActivityEnergyInfoAsync(in IOnWifiActivityEnergyInfoListener listener);
ParceledListSlice getConfiguredNetworks(String packageName, String featureId);
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 9956901..729ef61 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -499,6 +499,13 @@
/**
* @hide
+ */
+ public boolean is6GHz() {
+ return ScanResult.is6GHz(frequency);
+ }
+
+ /**
+ * @hide
* TODO: makes real freq boundaries
*/
public static boolean is5GHz(int freq) {
@@ -506,6 +513,13 @@
}
/**
+ * @hide
+ */
+ public static boolean is6GHz(int freq) {
+ return freq > 5925 && freq < 7125;
+ }
+
+ /**
* @hide
* anqp lines from supplicant BSS response
*/
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 8030bd6..d755053 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -388,7 +388,7 @@
@NonNull
public Builder setBssid(@Nullable MacAddress bssid) {
if (bssid != null) {
- Preconditions.checkArgument(!bssid.equals(MacAddress.ALL_ZEROS_ADDRESS));
+ Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
}
mBssid = bssid;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 52f5eee..0108d5a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -35,6 +35,7 @@
import android.content.pm.ParceledListSlice;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
+import android.net.MacAddress;
import android.net.Network;
import android.net.NetworkStack;
import android.net.wifi.hotspot2.IProvisioningCallback;
@@ -48,6 +49,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -1186,6 +1188,10 @@
/** Indicates an invalid SSID. */
public static final String UNKNOWN_SSID = "<unknown ssid>";
+ /** @hide */
+ public static final MacAddress ALL_ZEROS_MAC_ADDRESS =
+ MacAddress.fromString("00:00:00:00:00:00");
+
/* Number of currently active WifiLocks and MulticastLocks */
@UnsupportedAppUsage
private int mActiveLockCount;
@@ -1241,6 +1247,7 @@
* <li>allowedAuthAlgorithms</li>
* <li>allowedPairwiseCiphers</li>
* <li>allowedGroupCiphers</li>
+ * <li>status</li>
* </ul>
* @return a list of network configurations in the form of a list
* of {@link WifiConfiguration} objects.
@@ -2391,6 +2398,82 @@
}
/**
+ * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and
+ * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface OnWifiActivityEnergyInfoListener {
+ /**
+ * Called when Wi-Fi activity energy info is available.
+ * Note: this listener is triggered at most once for each call to
+ * {@link #getWifiActivityEnergyInfoAsync}.
+ *
+ * @param info the latest {@link WifiActivityEnergyInfo}, or null if unavailable.
+ */
+ void onWifiActivityEnergyInfo(@Nullable WifiActivityEnergyInfo info);
+ }
+
+ private static class OnWifiActivityEnergyInfoProxy
+ extends IOnWifiActivityEnergyInfoListener.Stub {
+ private final Object mLock = new Object();
+ @Nullable @GuardedBy("mLock") private Executor mExecutor;
+ @Nullable @GuardedBy("mLock") private OnWifiActivityEnergyInfoListener mListener;
+
+ OnWifiActivityEnergyInfoProxy(Executor executor,
+ OnWifiActivityEnergyInfoListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onWifiActivityEnergyInfo(WifiActivityEnergyInfo info) {
+ Executor executor;
+ OnWifiActivityEnergyInfoListener listener;
+ synchronized (mLock) {
+ if (mExecutor == null || mListener == null) {
+ return;
+ }
+ executor = mExecutor;
+ listener = mListener;
+ // null out to allow garbage collection, prevent triggering listener more than once
+ mExecutor = null;
+ mListener = null;
+ }
+ Binder.clearCallingIdentity();
+ executor.execute(() -> listener.onWifiActivityEnergyInfo(info));
+ }
+ }
+
+ /**
+ * Request to get the current {@link WifiActivityEnergyInfo} asynchronously.
+ * Note: This method will return null if {@link #isEnhancedPowerReportingSupported()} returns
+ * false.
+ *
+ * @param executor the executor that the listener will be invoked on
+ * @param listener the listener that will receive the {@link WifiActivityEnergyInfo} object
+ * when it becomes available. The listener will be triggered at most once for
+ * each call to this method.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public void getWifiActivityEnergyInfoAsync(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnWifiActivityEnergyInfoListener listener) {
+ if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+ try {
+ mService.getWifiActivityEnergyInfoAsync(
+ new OnWifiActivityEnergyInfoProxy(executor, listener));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Request a scan for access points. Returns immediately. The availability
* of the results is made known later by means of an asynchronous event sent
* on completion of the scan.
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index 5a212a8..be37c22 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.net.INetworkScoreCache;
import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
import android.net.ScoredNetwork;
import android.os.Handler;
import android.os.Process;
@@ -39,10 +38,10 @@
/**
* {@link INetworkScoreCache} implementation for Wifi Networks.
*
+ * TODO: This should not be part of wifi mainline module.
* @hide
*/
-public class WifiNetworkScoreCache extends INetworkScoreCache.Stub
- implements NetworkScoreManager.NetworkScoreCallback {
+public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
private static final String TAG = "WifiNetworkScoreCache";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
@@ -248,17 +247,6 @@
}
@Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
- WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- dumpWithLatestScanResults(fd, writer, args, wifiManager.getScanResults());
- }
-
- /**
- * This is directly invoked from within Wifi-Service (on it's instance of this class), hence
- * avoid making the WifiManager.getScanResults() call to avoid a deadlock.
- */
- public final void dumpWithLatestScanResults(
- FileDescriptor fd, PrintWriter writer, String[] args,
- List<ScanResult> latestScanResults) {
mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
String header = String.format("WifiNetworkScoreCache (%s/%d)",
mContext.getPackageName(), Process.myUid());
@@ -269,7 +257,8 @@
writer.println(" " + score);
}
writer.println(" Network scores for latest ScanResults:");
- for (ScanResult scanResult : latestScanResults) {
+ WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ for (ScanResult scanResult : wifiManager.getScanResults()) {
writer.println(
" " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index ba9dd37..07afd7f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -49,11 +49,11 @@
private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
- new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
- new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+ new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, MacAddress.BROADCAST_ADDRESS);
private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
- new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
MacAddress.BROADCAST_ADDRESS;
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index a5ca82c..e78104d 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -545,7 +545,7 @@
}
if (mBssid != null
&& (mBssid.equals(MacAddress.BROADCAST_ADDRESS)
- || mBssid.equals(MacAddress.ALL_ZEROS_ADDRESS))) {
+ || mBssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS))) {
throw new IllegalStateException("invalid bssid for suggestion");
}
wifiConfiguration = buildWifiConfiguration();
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index b293077..760497b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -65,14 +65,17 @@
/** @hide */
public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2;
/** @hide */
- public static final int WIFI_BAND_COUNT = 3;
+ public static final int WIFI_BAND_INDEX_6_GHZ = 3;
+ /** @hide */
+ public static final int WIFI_BAND_COUNT = 4;
/** @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})
+ WIFI_BAND_INDEX_5_GHZ_DFS_ONLY,
+ WIFI_BAND_INDEX_6_GHZ})
public @interface WifiBandIndex {}
/** no band specified; use channel list instead */
@@ -83,6 +86,8 @@
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;
+ /** 6 GHz band */
+ public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -90,7 +95,8 @@
WIFI_BAND_UNSPECIFIED,
WIFI_BAND_24_GHZ,
WIFI_BAND_5_GHZ,
- WIFI_BAND_5_GHZ_DFS_ONLY})
+ WIFI_BAND_5_GHZ_DFS_ONLY,
+ WIFI_BAND_6_GHZ})
public @interface WifiBandBasic {}
/**
@@ -111,6 +117,11 @@
/** 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;
+ /** 2.4 GHz band and 5 GHz band (no DFS channels) and 6 GHz */
+ public static final int WIFI_BAND_24_5_6_GHZ = WIFI_BAND_BOTH | WIFI_BAND_6_GHZ;
+ /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */
+ public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ =
+ WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -122,14 +133,17 @@
WIFI_BAND_5_GHZ_DFS_ONLY,
WIFI_BAND_24_GHZ_WITH_5GHZ_DFS,
WIFI_BAND_5_GHZ_WITH_DFS,
- WIFI_BAND_BOTH_WITH_DFS})
+ WIFI_BAND_BOTH_WITH_DFS,
+ WIFI_BAND_6_GHZ,
+ WIFI_BAND_24_5_6_GHZ,
+ WIFI_BAND_24_5_WITH_DFS_6_GHZ})
public @interface WifiBand {}
/**
* Max band value
* @hide
*/
- public static final int WIFI_BAND_MAX = 8;
+ public static final int WIFI_BAND_MAX = 0x10;
/** Minimum supported scanning period */
public static final int MIN_SCAN_PERIOD_MS = 1000;
@@ -174,7 +188,7 @@
@SystemApi
@NonNull
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
- public List<Integer> getAvailableChannels(@WifiBand int band) {
+ public List<Integer> getAvailableChannels(int band) {
try {
Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -726,6 +740,8 @@
public int min5GHzRssi;
/** Minimum 2.4GHz RSSI for a BSSID to be considered */
public int min24GHzRssi;
+ /** Minimum 6GHz RSSI for a BSSID to be considered */
+ public int min6GHzRssi;
/** Maximum score that a network can have before bonuses */
public int initialScoreMax;
/**
@@ -739,6 +755,8 @@
public int secureBonus;
/** 5GHz RSSI score bonus (applied to all 5GHz networks) */
public int band5GHzBonus;
+ /** 6GHz RSSI score bonus (applied to all 5GHz networks) */
+ public int band6GHzBonus;
/** Pno Network filter list */
public PnoNetwork[] networkList;
@@ -752,11 +770,13 @@
dest.writeInt(isConnected ? 1 : 0);
dest.writeInt(min5GHzRssi);
dest.writeInt(min24GHzRssi);
+ dest.writeInt(min6GHzRssi);
dest.writeInt(initialScoreMax);
dest.writeInt(currentConnectionBonus);
dest.writeInt(sameNetworkBonus);
dest.writeInt(secureBonus);
dest.writeInt(band5GHzBonus);
+ dest.writeInt(band6GHzBonus);
if (networkList != null) {
dest.writeInt(networkList.length);
for (int i = 0; i < networkList.length; i++) {
@@ -778,11 +798,13 @@
settings.isConnected = in.readInt() == 1;
settings.min5GHzRssi = in.readInt();
settings.min24GHzRssi = in.readInt();
+ settings.min6GHzRssi = in.readInt();
settings.initialScoreMax = in.readInt();
settings.currentConnectionBonus = in.readInt();
settings.sameNetworkBonus = in.readInt();
settings.secureBonus = in.readInt();
settings.band5GHzBonus = in.readInt();
+ settings.band6GHzBonus = in.readInt();
int numNetworks = in.readInt();
settings.networkList = new PnoNetwork[numNetworks];
for (int i = 0; i < numNetworks; i++) {
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index bc86839..367cfa0 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -25,6 +25,7 @@
import android.net.wifi.IDppCallback;
import android.net.wifi.ILocalOnlyHotspotCallback;
import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.IOnWifiActivityEnergyInfoListener;
import android.net.wifi.IOnWifiUsabilityStatsListener;
import android.net.wifi.IScanResultsCallback;
import android.net.wifi.IScanResultsListener;
@@ -35,7 +36,6 @@
import android.net.wifi.IWifiManager;
import android.net.wifi.ScanResult;
import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiActivityEnergyInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiNetworkSuggestion;
@@ -46,6 +46,7 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
import java.util.List;
import java.util.Map;
@@ -80,12 +81,16 @@
throw new UnsupportedOperationException();
}
- @Override
public void requestActivityInfo(ResultReceiver result) {
throw new UnsupportedOperationException();
}
@Override
+ public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) {
throw new UnsupportedOperationException();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index b3a73bc..de45149 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -16,15 +16,31 @@
package android.net.wifi;
+import static android.net.wifi.WifiManager.ActionListener;
+import static android.net.wifi.WifiManager.BUSY;
+import static android.net.wifi.WifiManager.ERROR;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
+import static android.net.wifi.WifiManager.NOT_AUTHORIZED;
+import static android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
+import static android.net.wifi.WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
+import static android.net.wifi.WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+import static android.net.wifi.WifiManager.TxPacketCountListener;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_P2P;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
+import static android.net.wifi.WifiManager.WpsCallback;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -71,6 +87,7 @@
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
@@ -113,6 +130,7 @@
@Mock TrafficStateCallback mTrafficStateCallback;
@Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
@Mock OnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener;
+ @Mock OnWifiActivityEnergyInfoListener mOnWifiActivityEnergyInfoListener;
@Mock SuggestionConnectionStatusListener mListener;
@Mock Runnable mRunnable;
@Mock Executor mExecutor;
@@ -124,6 +142,7 @@
private WifiManager mWifiManager;
private WifiNetworkSuggestion mWifiNetworkSuggestion;
private ScanResultsCallback mScanResultsCallback;
+ private WifiActivityEnergyInfo mWifiActivityEnergyInfo;
@Before
public void setUp() throws Exception {
@@ -142,6 +161,7 @@
mRunnable.run();
}
};
+ mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0);
}
/**
@@ -1069,7 +1089,7 @@
}
- class WpsCallbackTester extends WifiManager.WpsCallback {
+ class WpsCallbackTester extends WpsCallback {
public boolean mStarted = false;
public boolean mSucceeded = false;
public boolean mFailed = false;
@@ -1101,7 +1121,7 @@
WpsCallbackTester wpsCallback = new WpsCallbackTester();
mWifiManager.startWps(null, wpsCallback);
assertTrue(wpsCallback.mFailed);
- assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode);
+ assertEquals(ERROR, wpsCallback.mFailureCode);
assertFalse(wpsCallback.mStarted);
assertFalse(wpsCallback.mSucceeded);
verifyNoMoreInteractions(mWifiService);
@@ -1124,7 +1144,7 @@
WpsCallbackTester wpsCallback = new WpsCallbackTester();
mWifiManager.cancelWps(wpsCallback);
assertTrue(wpsCallback.mFailed);
- assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode);
+ assertEquals(ERROR, wpsCallback.mFailureCode);
assertFalse(wpsCallback.mStarted);
assertFalse(wpsCallback.mSucceeded);
verifyNoMoreInteractions(mWifiService);
@@ -1437,13 +1457,13 @@
public void addGetRemoveNetworkSuggestions() throws Exception {
List<WifiNetworkSuggestion> testList = new ArrayList<>();
when(mWifiService.addNetworkSuggestions(any(List.class), anyString(),
- nullable(String.class))).thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+ nullable(String.class))).thenReturn(STATUS_NETWORK_SUGGESTIONS_SUCCESS);
when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(
- WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+ STATUS_NETWORK_SUGGESTIONS_SUCCESS);
when(mWifiService.getNetworkSuggestions(anyString()))
.thenReturn(testList);
- assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiManager.addNetworkSuggestions(testList));
verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME),
nullable(String.class));
@@ -1451,7 +1471,7 @@
assertEquals(testList, mWifiManager.getNetworkSuggestions());
verify(mWifiService).getNetworkSuggestions(eq(TEST_PACKAGE_NAME));
- assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+ assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS,
mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
}
@@ -1515,10 +1535,10 @@
@Test
public void testIsEnhancedOpenSupported() throws Exception {
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(WifiManager.WIFI_FEATURE_OWE));
+ .thenReturn(new Long(WIFI_FEATURE_OWE));
assertTrue(mWifiManager.isEnhancedOpenSupported());
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(~WifiManager.WIFI_FEATURE_OWE));
+ .thenReturn(new Long(~WIFI_FEATURE_OWE));
assertFalse(mWifiManager.isEnhancedOpenSupported());
}
@@ -1528,10 +1548,10 @@
@Test
public void testIsWpa3SaeSupported() throws Exception {
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SAE));
+ .thenReturn(new Long(WIFI_FEATURE_WPA3_SAE));
assertTrue(mWifiManager.isWpa3SaeSupported());
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SAE));
+ .thenReturn(new Long(~WIFI_FEATURE_WPA3_SAE));
assertFalse(mWifiManager.isWpa3SaeSupported());
}
@@ -1541,10 +1561,10 @@
@Test
public void testIsWpa3SuiteBSupported() throws Exception {
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+ .thenReturn(new Long(WIFI_FEATURE_WPA3_SUITE_B));
assertTrue(mWifiManager.isWpa3SuiteBSupported());
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+ .thenReturn(new Long(~WIFI_FEATURE_WPA3_SUITE_B));
assertFalse(mWifiManager.isWpa3SuiteBSupported());
}
@@ -1554,10 +1574,10 @@
@Test
public void testIsEasyConnectSupported() throws Exception {
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(WifiManager.WIFI_FEATURE_DPP));
+ .thenReturn(new Long(WIFI_FEATURE_DPP));
assertTrue(mWifiManager.isEasyConnectSupported());
when(mWifiService.getSupportedFeatures())
- .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP));
+ .thenReturn(new Long(~WIFI_FEATURE_DPP));
assertFalse(mWifiManager.isEasyConnectSupported());
}
@@ -1666,9 +1686,9 @@
@Test
public void testGetSupportedFeatures() throws Exception {
long supportedFeatures =
- WifiManager.WIFI_FEATURE_SCANNER
- | WifiManager.WIFI_FEATURE_PASSPOINT
- | WifiManager.WIFI_FEATURE_P2P;
+ WIFI_FEATURE_SCANNER
+ | WIFI_FEATURE_PASSPOINT
+ | WIFI_FEATURE_P2P;
when(mWifiService.getSupportedFeatures())
.thenReturn(Long.valueOf(supportedFeatures));
@@ -1700,6 +1720,58 @@
}
/**
+ * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync}
+ * throws an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetWifiActivityInfoNullExecutor() throws Exception {
+ mWifiManager.getWifiActivityEnergyInfoAsync(null, mOnWifiActivityEnergyInfoListener);
+ }
+
+ /**
+ * Tests that passing a null listener to {@link WifiManager#getWifiActivityEnergyInfoAsync}
+ * throws an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testGetWifiActivityInfoNullListener() throws Exception {
+ mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, null);
+ }
+
+ /** Tests that the listener runs on the correct Executor. */
+ @Test
+ public void testGetWifiActivityInfoRunsOnCorrectExecutor() throws Exception {
+ mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, mOnWifiActivityEnergyInfoListener);
+ ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor =
+ ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class);
+ verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture());
+ IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue();
+ listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+ verify(mExecutor).execute(any());
+
+ // ensure that the executor is only triggered once
+ listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+ verify(mExecutor).execute(any());
+ }
+
+ /** Tests that the correct listener runs. */
+ @Test
+ public void testGetWifiActivityInfoRunsCorrectListener() throws Exception {
+ int[] flag = {0};
+ mWifiManager.getWifiActivityEnergyInfoAsync(
+ new SynchronousExecutor(), info -> flag[0]++);
+ ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor =
+ ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class);
+ verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture());
+ IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue();
+ listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+ assertEquals(1, flag[0]);
+
+ // ensure that the listener is only triggered once
+ listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+ assertEquals(1, flag[0]);
+ }
+
+ /**
* Test behavior of {@link WifiManager#getConnectionInfo()}
*/
@Test
@@ -1756,11 +1828,11 @@
}
/**
- * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ * Test behavior of {@link WifiManager#connect(int, ActionListener)}
*/
@Test
public void testConnectWithListener() throws Exception {
- WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ ActionListener externalListener = mock(ActionListener.class);
mWifiManager.connect(TEST_NETWORK_ID, externalListener);
ArgumentCaptor<IActionListener> binderListenerCaptor =
@@ -1775,43 +1847,43 @@
verify(externalListener).onSuccess();
// Trigger on failure.
- binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+ binderListenerCaptor.getValue().onFailure(BUSY);
mLooper.dispatchAll();
- verify(externalListener).onFailure(WifiManager.BUSY);
+ verify(externalListener).onFailure(BUSY);
}
/**
- * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ * Test behavior of {@link WifiManager#connect(int, ActionListener)}
*/
@Test
public void testConnectWithListenerHandleSecurityException() throws Exception {
doThrow(new SecurityException()).when(mWifiService)
.connect(eq(null), anyInt(), any(IBinder.class),
any(IActionListener.class), anyInt());
- WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ ActionListener externalListener = mock(ActionListener.class);
mWifiManager.connect(TEST_NETWORK_ID, externalListener);
mLooper.dispatchAll();
- verify(externalListener).onFailure(WifiManager.NOT_AUTHORIZED);
+ verify(externalListener).onFailure(NOT_AUTHORIZED);
}
/**
- * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ * Test behavior of {@link WifiManager#connect(int, ActionListener)}
*/
@Test
public void testConnectWithListenerHandleRemoteException() throws Exception {
doThrow(new RemoteException()).when(mWifiService)
.connect(eq(null), anyInt(), any(IBinder.class),
any(IActionListener.class), anyInt());
- WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+ ActionListener externalListener = mock(ActionListener.class);
mWifiManager.connect(TEST_NETWORK_ID, externalListener);
mLooper.dispatchAll();
- verify(externalListener).onFailure(WifiManager.ERROR);
+ verify(externalListener).onFailure(ERROR);
}
/**
- * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+ * Test behavior of {@link WifiManager#connect(int, ActionListener)}
*/
@Test
public void testConnectWithoutListener() throws Exception {
@@ -1823,12 +1895,12 @@
}
/**
- * Test behavior of {@link WifiManager#getTxPacketCount(WifiManager.TxPacketCountListener)}
+ * Test behavior of {@link WifiManager#getTxPacketCount(TxPacketCountListener)}
*/
@Test
public void testGetTxPacketCount() throws Exception {
- WifiManager.TxPacketCountListener externalListener =
- mock(WifiManager.TxPacketCountListener.class);
+ TxPacketCountListener externalListener =
+ mock(TxPacketCountListener.class);
mWifiManager.getTxPacketCount(externalListener);
ArgumentCaptor<ITxPacketCountListener> binderListenerCaptor =
@@ -1843,9 +1915,9 @@
verify(externalListener).onSuccess(6);
// Trigger on failure.
- binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+ binderListenerCaptor.getValue().onFailure(BUSY);
mLooper.dispatchAll();
- verify(externalListener).onFailure(WifiManager.BUSY);
+ verify(externalListener).onFailure(BUSY);
}
/**
@@ -1947,7 +2019,7 @@
*/
@Test
public void testAddSuggestionConnectionStatusListenerAndReceiveEvent() throws Exception {
- int errorCode = WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+ int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class);
Executor executor = new SynchronousExecutor();
@@ -1963,7 +2035,7 @@
*/
@Test
public void testAddSuggestionConnectionStatusListenerWithTheTargetExecutor() throws Exception {
- int errorCode = WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+ int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor =
ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class);
mWifiManager.addSuggestionConnectionStatusListener(mExecutor, mListener);
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index e6eece8..adc41f0 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -208,7 +208,7 @@
PatternMatcher ssidPattern =
new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
Pair<MacAddress, MacAddress> bssidPattern =
- Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
wificonfigurationNetworkSpecifier.allowedKeyManagement
.set(WifiConfiguration.KeyMgmt.WPA_PSK);
@@ -299,7 +299,7 @@
PatternMatcher ssidPattern =
new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
Pair<MacAddress, MacAddress> bssidPattern =
- Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+ Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
wificonfigurationNetworkSpecifier.allowedKeyManagement
.set(WifiConfiguration.KeyMgmt.WPA_PSK);
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index edb43d8..1619744 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -65,8 +65,10 @@
assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid);
assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType());
- assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.first);
- assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second);
+ assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+ wifiNetworkSpecifier.bssidPatternMatcher.first);
+ assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+ wifiNetworkSpecifier.bssidPatternMatcher.second);
assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
.get(WifiConfiguration.KeyMgmt.NONE));
}
@@ -210,7 +212,8 @@
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() {
new WifiNetworkSpecifier.Builder()
- .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS)
+ .setBssidPattern(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+ WifiManager.ALL_ZEROS_MAC_ADDRESS)
.build();
}
@@ -265,7 +268,7 @@
@Test(expected = IllegalStateException.class)
public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
new WifiNetworkSpecifier.Builder()
- .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS)
.build();
}
@@ -513,7 +516,8 @@
WifiNetworkSpecifier specifier2 =
new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
- Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+ Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+ WifiManager.ALL_ZEROS_MAC_ADDRESS),
wifiConfiguration,
TEST_UID, TEST_PACKAGE_NAME);
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index ce085f5..8a5a0fd 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -294,7 +294,7 @@
public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
new WifiNetworkSuggestion.Builder()
.setSsid(TEST_SSID)
- .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+ .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS)
.build();
}
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index b1436c90..fa4f711 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -79,15 +79,17 @@
private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false;
private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70;
+ private static final int TEST_PNOSETTINGS_MIN_6GHZ_RSSI = -55;
private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50;
private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10;
private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11;
private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12;
private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13;
+ private static final int TEST_PNOSETTINGS_BAND_6GHZ_BONUS = 15;
private static final String TEST_SSID_1 = "TEST1";
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 int[] TEST_FREQUENCIES_2 = {2500, 5124, 6245};
private static final String DESCRIPTION_NOT_AUTHORIZED = "Not authorized";
private WifiScanner mWifiScanner;
@@ -183,11 +185,13 @@
pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED;
pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI;
pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI;
+ pnoSettings.min6GHzRssi = TEST_PNOSETTINGS_MIN_6GHZ_RSSI;
pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX;
pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS;
pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS;
pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS;
pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS;
+ pnoSettings.band6GHzBonus = TEST_PNOSETTINGS_BAND_6GHZ_BONUS;
Parcel parcel = Parcel.obtain();
pnoSettings.writeToParcel(parcel, 0);
@@ -200,6 +204,7 @@
assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected);
assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi);
assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi);
+ assertEquals(TEST_PNOSETTINGS_MIN_6GHZ_RSSI, pnoSettingsDeserialized.min6GHzRssi);
assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax);
assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS,
pnoSettingsDeserialized.currentConnectionBonus);
@@ -207,6 +212,7 @@
pnoSettingsDeserialized.sameNetworkBonus);
assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus);
assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus);
+ assertEquals(TEST_PNOSETTINGS_BAND_6GHZ_BONUS, pnoSettingsDeserialized.band6GHzBonus);
// Test parsing of PnoNetwork
assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length);